@gobing-ai/ts-dual-workflow-engine 0.4.32 → 0.4.35

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.
@@ -1,7 +1,7 @@
1
1
  import type { WorkflowEngineHost } from './host';
2
2
  import type { RunLifecycle } from './run-lifecycle';
3
3
  import { type WorkflowMode } from './run-lifecycle';
4
- import type { ActionDef, ActionResult, OnErrorPolicy, WorkflowPersistenceAdapter, WorkflowRunOptions } from './types';
4
+ import type { ActionDef, ActionResult, OnErrorPolicy, Vars, WorkflowPersistenceAdapter, WorkflowRunOptions } from './types';
5
5
  /**
6
6
  * Outcome discriminator for a single action step. `terminal` means the action
7
7
  * declared terminal success (the run is done); `fail` means a failure under a
@@ -9,10 +9,17 @@ import type { ActionDef, ActionResult, OnErrorPolicy, WorkflowPersistenceAdapter
9
9
  * a failure that the 'continue' policy logged past) and the loop may proceed.
10
10
  */
11
11
  export type ActionStepOutcome = 'completed' | 'terminal' | 'fail';
12
- /** Result of running one or more action steps: last result plus a control-flow discriminator. */
12
+ /** Result of running one or more action steps: last result, a control-flow discriminator, and the accumulated `setVars` of every step in the sequence. */
13
13
  export interface ActionStepResult {
14
14
  readonly outcome: ActionStepOutcome;
15
15
  readonly result: ActionResult | undefined;
16
+ /**
17
+ * Sequence-level accumulation of `setVars` from every action that ran
18
+ * (including the terminal/failing one). Single-step callers get just that
19
+ * step's setVars. Drivers merge this map into their run-local vars after
20
+ * the sequence settles.
21
+ */
22
+ readonly setVars?: Vars;
16
23
  }
17
24
  /**
18
25
  * Everything an action step needs that does not change between the two drivers:
@@ -38,9 +45,9 @@ export interface ActionStepDeps {
38
45
  * Run one action through its full lifecycle: resolve templates, persist the
39
46
  * start row, time the host invocation inside try/finally, persist + emit the
40
47
  * finalize, then classify terminal/fail/continue. `vars` is read-only here;
41
- * callers thread any `setVars` from the returned result back into their own
42
- * vars map (the merge stays caller-owned because the two drivers carry vars
43
- * differently across their loops).
48
+ * callers thread any `setVars` from the returned result forward the sequence
49
+ * runner does so within a sequence, and drivers merge the accumulated map
50
+ * across states/nodes (each dialect carries vars differently across its loop).
44
51
  *
45
52
  * This is the single seam both drivers share for action execution (ADR-006 §7
46
53
  * keeps the *control loops* dialect-specific; only this per-action mechanism is
@@ -49,13 +56,15 @@ export interface ActionStepDeps {
49
56
  export declare function runActionStep(action: ActionDef, vars: Record<string, string>, deps: ActionStepDeps): Promise<ActionStepResult>;
50
57
  /**
51
58
  * Run a sequence of actions in declaration order, stopping at the first
52
- * `terminal` or `fail` outcome. Every action resolves its templates against the
53
- * same `vars` snapshot the caller passed in `setVars` does not affect later
54
- * actions within the same sequence; it is the caller (the state-machine driver)
55
- * that merges the returned result's `setVars` into its run-local vars after the
56
- * sequence settles. Returns the last action result (retained even when a failure
57
- * was continued past, so downstream guards can inspect it) plus the controlling
58
- * outcome.
59
+ * `terminal` or `fail` outcome. `setVars` from each action is threaded forward
60
+ * BEFORE the next action runs, so action N+1 resolves its templates (and sees
61
+ * `context.vars`) with every prior same-sequence `setVars` applied the YAML
62
+ * reads imperatively. The returned `setVars` accumulates the maps of every
63
+ * action that ran (including a continued-past failure and the terminal/failing
64
+ * action itself); it is the caller (the state-machine driver) that merges it
65
+ * into run-local vars after the sequence settles. Returns the last action
66
+ * result (retained even when a failure was continued past, so downstream
67
+ * guards can inspect it) plus the controlling outcome.
59
68
  */
60
69
  export declare function runActionSequence(actions: readonly ActionDef[], vars: Record<string, string>, deps: ActionStepDeps): Promise<ActionStepResult>;
61
70
  //# sourceMappingURL=action-step.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-step.d.ts","sourceRoot":"","sources":["../src/action-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGtH;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,iGAAiG;AACjG,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;IACjD,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,aAAa,GAAG,SAAS,CAAC;CACtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAC/B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAkD3B;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACnC,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAS3B"}
1
+ {"version":3,"file":"action-step.d.ts","sourceRoot":"","sources":["../src/action-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EACR,SAAS,EACT,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,0BAA0B,EAC1B,kBAAkB,EACrB,MAAM,SAAS,CAAC;AAGjB;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,0JAA0J;AAC1J,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;IACjD,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,aAAa,GAAG,SAAS,CAAC;CACtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAC/B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAkD3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACnC,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAc3B"}
@@ -1,13 +1,13 @@
1
1
  import { defaultActionRedactor } from './persistence.js';
2
2
  import { runtimeBuiltins } from './run-lifecycle.js';
3
- import { resolveOnErrorPolicy, resolveTemplates } from './variables.js';
3
+ import { mergeSetVars, resolveOnErrorPolicy, resolveTemplates } from './variables.js';
4
4
  /**
5
5
  * Run one action through its full lifecycle: resolve templates, persist the
6
6
  * start row, time the host invocation inside try/finally, persist + emit the
7
7
  * finalize, then classify terminal/fail/continue. `vars` is read-only here;
8
- * callers thread any `setVars` from the returned result back into their own
9
- * vars map (the merge stays caller-owned because the two drivers carry vars
10
- * differently across their loops).
8
+ * callers thread any `setVars` from the returned result forward the sequence
9
+ * runner does so within a sequence, and drivers merge the accumulated map
10
+ * across states/nodes (each dialect carries vars differently across its loop).
11
11
  *
12
12
  * This is the single seam both drivers share for action execution (ADR-006 §7
13
13
  * keeps the *control loops* dialect-specific; only this per-action mechanism is
@@ -60,24 +60,31 @@ export async function runActionStep(action, vars, deps) {
60
60
  }
61
61
  /**
62
62
  * Run a sequence of actions in declaration order, stopping at the first
63
- * `terminal` or `fail` outcome. Every action resolves its templates against the
64
- * same `vars` snapshot the caller passed in `setVars` does not affect later
65
- * actions within the same sequence; it is the caller (the state-machine driver)
66
- * that merges the returned result's `setVars` into its run-local vars after the
67
- * sequence settles. Returns the last action result (retained even when a failure
68
- * was continued past, so downstream guards can inspect it) plus the controlling
69
- * outcome.
63
+ * `terminal` or `fail` outcome. `setVars` from each action is threaded forward
64
+ * BEFORE the next action runs, so action N+1 resolves its templates (and sees
65
+ * `context.vars`) with every prior same-sequence `setVars` applied the YAML
66
+ * reads imperatively. The returned `setVars` accumulates the maps of every
67
+ * action that ran (including a continued-past failure and the terminal/failing
68
+ * action itself); it is the caller (the state-machine driver) that merges it
69
+ * into run-local vars after the sequence settles. Returns the last action
70
+ * result (retained even when a failure was continued past, so downstream
71
+ * guards can inspect it) plus the controlling outcome.
70
72
  */
71
73
  export async function runActionSequence(actions, vars, deps) {
72
74
  let last;
75
+ let acc;
73
76
  for (const action of actions) {
74
77
  const step = await runActionStep(action, vars, deps);
75
78
  if (step.result !== undefined)
76
79
  last = step.result;
80
+ if (step.result?.setVars) {
81
+ acc = mergeSetVars(acc ?? {}, step.result.setVars);
82
+ vars = mergeSetVars(vars, step.result.setVars);
83
+ }
77
84
  if (step.outcome === 'terminal')
78
- return { outcome: 'terminal', result: last };
85
+ return { outcome: 'terminal', result: last, setVars: acc };
79
86
  if (step.outcome === 'fail')
80
- return { outcome: 'fail', result: last };
87
+ return { outcome: 'fail', result: last, setVars: acc };
81
88
  }
82
- return { outcome: 'completed', result: last };
89
+ return { outcome: 'completed', result: last, setVars: acc };
83
90
  }
@@ -69,8 +69,8 @@ export class StateMachineDriver {
69
69
  // erase the previous result.
70
70
  if (enter.result !== undefined)
71
71
  lastActionResult = enter.result;
72
- if (enter.result?.setVars)
73
- vars = mergeSetVars(vars, enter.result.setVars);
72
+ if (enter.setVars)
73
+ vars = mergeSetVars(vars, enter.setVars);
74
74
  if (enter.outcome === 'terminal') {
75
75
  return failure.has(current.id)
76
76
  ? await lifecycle.fail(current.id, transitionsTaken, `terminal:${current.id}`)
@@ -122,8 +122,8 @@ export class StateMachineDriver {
122
122
  });
123
123
  if (exit.result !== undefined)
124
124
  lastActionResult = exit.result;
125
- if (exit.result?.setVars)
126
- vars = mergeSetVars(vars, exit.result.setVars);
125
+ if (exit.setVars)
126
+ vars = mergeSetVars(vars, exit.setVars);
127
127
  if (exit.outcome === 'fail')
128
128
  return await lifecycle.fail(current.id, transitionsTaken, exit.result?.error);
129
129
  // 7. Atomically commit the transition + new state snapshot + phase in a
@@ -146,7 +146,7 @@ export class StateMachineDriver {
146
146
  }
147
147
  }
148
148
  /** Dry-run sentinel: no action ran, so there is nothing to retain and nothing to halt on. */
149
- const EMPTY_OUTCOME = { outcome: 'completed', result: undefined };
149
+ const EMPTY_OUTCOME = { outcome: 'completed', result: undefined, setVars: undefined };
150
150
  async function firstPassingTransition(transitions, host, context, lifecycle) {
151
151
  for (const transition of transitions) {
152
152
  if (transition.guard === undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-dual-workflow-engine",
3
- "version": "0.4.32",
3
+ "version": "0.4.35",
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.32",
52
- "@gobing-ai/ts-infra": "^0.4.32",
53
- "@gobing-ai/ts-runtime": "^0.4.32",
51
+ "@gobing-ai/ts-db": "^0.4.35",
52
+ "@gobing-ai/ts-infra": "^0.4.35",
53
+ "@gobing-ai/ts-runtime": "^0.4.35",
54
54
  "zod": "^4.1.0"
55
55
  },
56
56
  "devDependencies": {
@@ -2,8 +2,15 @@ import type { WorkflowEngineHost } from './host';
2
2
  import { defaultActionRedactor } from './persistence';
3
3
  import type { RunLifecycle } from './run-lifecycle';
4
4
  import { runtimeBuiltins, type WorkflowMode } from './run-lifecycle';
5
- import type { ActionDef, ActionResult, OnErrorPolicy, WorkflowPersistenceAdapter, WorkflowRunOptions } from './types';
6
- import { resolveOnErrorPolicy, resolveTemplates } from './variables';
5
+ import type {
6
+ ActionDef,
7
+ ActionResult,
8
+ OnErrorPolicy,
9
+ Vars,
10
+ WorkflowPersistenceAdapter,
11
+ WorkflowRunOptions,
12
+ } from './types';
13
+ import { mergeSetVars, resolveOnErrorPolicy, resolveTemplates } from './variables';
7
14
 
8
15
  /**
9
16
  * Outcome discriminator for a single action step. `terminal` means the action
@@ -13,10 +20,17 @@ import { resolveOnErrorPolicy, resolveTemplates } from './variables';
13
20
  */
14
21
  export type ActionStepOutcome = 'completed' | 'terminal' | 'fail';
15
22
 
16
- /** Result of running one or more action steps: last result plus a control-flow discriminator. */
23
+ /** Result of running one or more action steps: last result, a control-flow discriminator, and the accumulated `setVars` of every step in the sequence. */
17
24
  export interface ActionStepResult {
18
25
  readonly outcome: ActionStepOutcome;
19
26
  readonly result: ActionResult | undefined;
27
+ /**
28
+ * Sequence-level accumulation of `setVars` from every action that ran
29
+ * (including the terminal/failing one). Single-step callers get just that
30
+ * step's setVars. Drivers merge this map into their run-local vars after
31
+ * the sequence settles.
32
+ */
33
+ readonly setVars?: Vars;
20
34
  }
21
35
 
22
36
  /**
@@ -44,9 +58,9 @@ export interface ActionStepDeps {
44
58
  * Run one action through its full lifecycle: resolve templates, persist the
45
59
  * start row, time the host invocation inside try/finally, persist + emit the
46
60
  * finalize, then classify terminal/fail/continue. `vars` is read-only here;
47
- * callers thread any `setVars` from the returned result back into their own
48
- * vars map (the merge stays caller-owned because the two drivers carry vars
49
- * differently across their loops).
61
+ * callers thread any `setVars` from the returned result forward the sequence
62
+ * runner does so within a sequence, and drivers merge the accumulated map
63
+ * across states/nodes (each dialect carries vars differently across its loop).
50
64
  *
51
65
  * This is the single seam both drivers share for action execution (ADR-006 §7
52
66
  * keeps the *control loops* dialect-specific; only this per-action mechanism is
@@ -110,13 +124,15 @@ export async function runActionStep(
110
124
 
111
125
  /**
112
126
  * Run a sequence of actions in declaration order, stopping at the first
113
- * `terminal` or `fail` outcome. Every action resolves its templates against the
114
- * same `vars` snapshot the caller passed in `setVars` does not affect later
115
- * actions within the same sequence; it is the caller (the state-machine driver)
116
- * that merges the returned result's `setVars` into its run-local vars after the
117
- * sequence settles. Returns the last action result (retained even when a failure
118
- * was continued past, so downstream guards can inspect it) plus the controlling
119
- * outcome.
127
+ * `terminal` or `fail` outcome. `setVars` from each action is threaded forward
128
+ * BEFORE the next action runs, so action N+1 resolves its templates (and sees
129
+ * `context.vars`) with every prior same-sequence `setVars` applied the YAML
130
+ * reads imperatively. The returned `setVars` accumulates the maps of every
131
+ * action that ran (including a continued-past failure and the terminal/failing
132
+ * action itself); it is the caller (the state-machine driver) that merges it
133
+ * into run-local vars after the sequence settles. Returns the last action
134
+ * result (retained even when a failure was continued past, so downstream
135
+ * guards can inspect it) plus the controlling outcome.
120
136
  */
121
137
  export async function runActionSequence(
122
138
  actions: readonly ActionDef[],
@@ -124,11 +140,16 @@ export async function runActionSequence(
124
140
  deps: ActionStepDeps,
125
141
  ): Promise<ActionStepResult> {
126
142
  let last: ActionResult | undefined;
143
+ let acc: Vars | undefined;
127
144
  for (const action of actions) {
128
145
  const step = await runActionStep(action, vars, deps);
129
146
  if (step.result !== undefined) last = step.result;
130
- if (step.outcome === 'terminal') return { outcome: 'terminal', result: last };
131
- if (step.outcome === 'fail') return { outcome: 'fail', result: last };
147
+ if (step.result?.setVars) {
148
+ acc = mergeSetVars(acc ?? {}, step.result.setVars);
149
+ vars = mergeSetVars(vars, step.result.setVars);
150
+ }
151
+ if (step.outcome === 'terminal') return { outcome: 'terminal', result: last, setVars: acc };
152
+ if (step.outcome === 'fail') return { outcome: 'fail', result: last, setVars: acc };
132
153
  }
133
- return { outcome: 'completed', result: last };
154
+ return { outcome: 'completed', result: last, setVars: acc };
134
155
  }
@@ -109,7 +109,7 @@ export class StateMachineDriver {
109
109
  // driver's `continue` semantics. A state with no enter actions must not
110
110
  // erase the previous result.
111
111
  if (enter.result !== undefined) lastActionResult = enter.result;
112
- if (enter.result?.setVars) vars = mergeSetVars(vars, enter.result.setVars);
112
+ if (enter.setVars) vars = mergeSetVars(vars, enter.setVars);
113
113
  if (enter.outcome === 'terminal') {
114
114
  return failure.has(current.id)
115
115
  ? await lifecycle.fail(current.id, transitionsTaken, `terminal:${current.id}`)
@@ -170,7 +170,7 @@ export class StateMachineDriver {
170
170
  defaultOnError,
171
171
  });
172
172
  if (exit.result !== undefined) lastActionResult = exit.result;
173
- if (exit.result?.setVars) vars = mergeSetVars(vars, exit.result.setVars);
173
+ if (exit.setVars) vars = mergeSetVars(vars, exit.setVars);
174
174
  if (exit.outcome === 'fail') return await lifecycle.fail(current.id, transitionsTaken, exit.result?.error);
175
175
 
176
176
  // 7. Atomically commit the transition + new state snapshot + phase in a
@@ -200,7 +200,7 @@ export class StateMachineDriver {
200
200
  }
201
201
 
202
202
  /** Dry-run sentinel: no action ran, so there is nothing to retain and nothing to halt on. */
203
- const EMPTY_OUTCOME = { outcome: 'completed', result: undefined } as const;
203
+ const EMPTY_OUTCOME = { outcome: 'completed', result: undefined, setVars: undefined } as const;
204
204
 
205
205
  async function firstPassingTransition(
206
206
  transitions: StateMachineWorkflowDef['transitions'],