@exaudeus/workrail 3.22.0 → 3.23.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.
@@ -81,13 +81,17 @@ let WorkflowInterpreter = class WorkflowInterpreter {
81
81
  continue;
82
82
  }
83
83
  }
84
- const top = this.nextTopLevel(compiled, running, context);
84
+ const top = this.nextTopLevel(compiled, running, context, trace);
85
85
  if (top.isErr())
86
86
  return (0, neverthrow_1.err)(top.error);
87
87
  const out = top.value;
88
88
  running = out.state;
89
89
  if (out.next) {
90
- trace.push((0, decision_trace_builder_1.traceSelectedNextStep)(out.next.stepInstanceId.stepId, out.next.step.title));
90
+ const selectedStep = out.next.step;
91
+ const label = selectedStep.runCondition
92
+ ? selectedStep.title ?? out.next.stepInstanceId.stepId
93
+ : `Selected (no condition): ${selectedStep.title ?? out.next.stepInstanceId.stepId}`;
94
+ trace.push((0, decision_trace_builder_1.traceSelectedNextStep)(out.next.stepInstanceId.stepId, label));
91
95
  return (0, neverthrow_1.ok)({ state: running, next: out.next, isComplete: false, trace });
92
96
  }
93
97
  if (running.loopStack.length > 0) {
@@ -106,14 +110,19 @@ let WorkflowInterpreter = class WorkflowInterpreter {
106
110
  }
107
111
  return (0, neverthrow_1.ok)(state);
108
112
  }
109
- nextTopLevel(compiled, state, context) {
113
+ nextTopLevel(compiled, state, context, trace = []) {
110
114
  for (const step of compiled.steps) {
111
115
  if (compiled.loopBodyStepIds.has(step.id))
112
116
  continue;
113
117
  if (state.completed.includes(step.id))
114
118
  continue;
115
- if (step.runCondition && !(0, condition_evaluator_1.evaluateCondition)(step.runCondition, context)) {
116
- continue;
119
+ if (step.runCondition) {
120
+ const conditionPassed = (0, condition_evaluator_1.evaluateCondition)(step.runCondition, context);
121
+ if (!conditionPassed) {
122
+ trace.push((0, decision_trace_builder_1.traceStepRunConditionSkipped)(step.id, step.title, step.runCondition, context));
123
+ continue;
124
+ }
125
+ trace.push((0, decision_trace_builder_1.traceStepRunConditionPassed)(step.id, step.title, step.runCondition, context));
117
126
  }
118
127
  if ((0, workflow_1.isLoopStepDefinition)(step)) {
119
128
  const entered = { loopId: step.id, iteration: 0, bodyIndex: 0 };
@@ -142,8 +142,8 @@
142
142
  "bytes": 1507
143
143
  },
144
144
  "application/services/workflow-interpreter.js": {
145
- "sha256": "ba498a0d1fd7a2f415c993e8a11b32002afca18f0cee932936331b7c4a079a66",
146
- "bytes": 21475
145
+ "sha256": "860a147051cb6cf36720c18a30c5c6f62c7868c4dd9a0f98e413fda0b0d205a5",
146
+ "bytes": 22145
147
147
  },
148
148
  "application/services/workflow-service.d.ts": {
149
149
  "sha256": "c9c9e2ab4396c46da0f12af93133ca1e7da94bdc88f67a074d8f6c43ef0a5b3b",
@@ -1622,12 +1622,12 @@
1622
1622
  "bytes": 962
1623
1623
  },
1624
1624
  "v2/durable-core/domain/decision-trace-builder.d.ts": {
1625
- "sha256": "17383d46067f3941b7a4c391a74a9d4c6cfb55d98edec5b40cd954831c4a2428",
1626
- "bytes": 1762
1625
+ "sha256": "f897dd17019bb094c72b1b19d7e731d535f66256ae38a0b08646d2604be0663d",
1626
+ "bytes": 2314
1627
1627
  },
1628
1628
  "v2/durable-core/domain/decision-trace-builder.js": {
1629
- "sha256": "4b34913677a333f78f739ba2ca95845dcc610364ca5d3eb7af9866ed889545ce",
1630
- "bytes": 4398
1629
+ "sha256": "478a50a6b530248a46359d589611c640683fd3f754c2412341b7affa61f4d95b",
1630
+ "bytes": 7910
1631
1631
  },
1632
1632
  "v2/durable-core/domain/function-definition-expander.d.ts": {
1633
1633
  "sha256": "2720e7c4dfbd9ec0cdf75eb4384fdbea18d4430aeae7f13d9215f0e5e4980468",
@@ -1,5 +1,6 @@
1
1
  import type { Result } from 'neverthrow';
2
2
  import type { LoopControlEvaluationResult } from './loop-control-evaluator.js';
3
+ import type { Condition } from '../../../utils/condition-evaluator.js';
3
4
  export type DecisionTraceEntryKind = 'selected_next_step' | 'evaluated_condition' | 'entered_loop' | 'exited_loop' | 'detected_non_tip_advance';
4
5
  export type DecisionTraceRef = {
5
6
  readonly kind: 'step_id';
@@ -23,6 +24,9 @@ export declare function traceEnteredLoop(loopId: string, iteration: number): Dec
23
24
  export declare function traceEvaluatedCondition(loopId: string, iteration: number, result: boolean, source: 'artifact' | 'context' | 'legacy'): DecisionTraceEntry;
24
25
  export declare function traceExitedLoop(loopId: string, reason: string): DecisionTraceEntry;
25
26
  export declare function traceSelectedNextStep(stepId: string, stepTitle?: string): DecisionTraceEntry;
27
+ export declare function formatConditionTrace(condition: Condition, context: Record<string, unknown>, passed: boolean): string;
28
+ export declare function traceStepRunConditionSkipped(stepId: string, stepTitle: string | undefined, condition: Condition, context: Record<string, unknown>): DecisionTraceEntry;
29
+ export declare function traceStepRunConditionPassed(stepId: string, stepTitle: string | undefined, condition: Condition, context: Record<string, unknown>): DecisionTraceEntry;
26
30
  export declare function traceArtifactMatchResult(loopId: string, iteration: number, result: LoopControlEvaluationResult): DecisionTraceEntry;
27
31
  export declare function applyTraceBudget(entries: readonly DecisionTraceEntry[]): readonly DecisionTraceEntry[];
28
32
  export declare function buildDecisionTraceEventData(traceId: string, entries: readonly DecisionTraceEntry[]): Result<{
@@ -4,6 +4,9 @@ exports.traceEnteredLoop = traceEnteredLoop;
4
4
  exports.traceEvaluatedCondition = traceEvaluatedCondition;
5
5
  exports.traceExitedLoop = traceExitedLoop;
6
6
  exports.traceSelectedNextStep = traceSelectedNextStep;
7
+ exports.formatConditionTrace = formatConditionTrace;
8
+ exports.traceStepRunConditionSkipped = traceStepRunConditionSkipped;
9
+ exports.traceStepRunConditionPassed = traceStepRunConditionPassed;
7
10
  exports.traceArtifactMatchResult = traceArtifactMatchResult;
8
11
  exports.applyTraceBudget = applyTraceBudget;
9
12
  exports.buildDecisionTraceEventData = buildDecisionTraceEventData;
@@ -40,6 +43,100 @@ function traceSelectedNextStep(stepId, stepTitle) {
40
43
  refs: [{ kind: 'step_id', stepId }],
41
44
  };
42
45
  }
46
+ function formatConditionTrace(condition, context, passed) {
47
+ const prefix = passed ? 'PASS' : 'SKIP';
48
+ if (condition.and !== undefined) {
49
+ return passed ? 'PASS: and-condition met' : 'SKIP: and-condition not met';
50
+ }
51
+ if (condition.or !== undefined) {
52
+ return passed ? 'PASS: or-condition met' : 'SKIP: or-condition not met';
53
+ }
54
+ if (condition.not !== undefined) {
55
+ return passed ? 'PASS: not-condition met' : 'SKIP: not-condition not met';
56
+ }
57
+ const varName = condition.var;
58
+ if (!varName) {
59
+ return `${prefix}: condition ${passed ? 'met' : 'not met'}`;
60
+ }
61
+ const op = getOperatorLabel(condition);
62
+ if (passed) {
63
+ const contextValue = context[varName];
64
+ const contextStr = contextValue === undefined ? '(unset)' : String(contextValue);
65
+ const expectedStr = getExpectedValueLabel(condition);
66
+ return expectedStr !== null
67
+ ? `PASS: ${varName}=${contextStr} (${op}: ${expectedStr})`
68
+ : `PASS: ${varName}=${contextStr} (${op})`;
69
+ }
70
+ else {
71
+ return `SKIP: ${varName} (${op})`;
72
+ }
73
+ }
74
+ function getOperatorLabel(condition) {
75
+ if (condition.var === undefined)
76
+ return 'condition';
77
+ if ('equals' in condition)
78
+ return 'equals';
79
+ if ('not_equals' in condition)
80
+ return 'not_equals';
81
+ if (condition.gt !== undefined)
82
+ return 'gt';
83
+ if (condition.gte !== undefined)
84
+ return 'gte';
85
+ if (condition.lt !== undefined)
86
+ return 'lt';
87
+ if (condition.lte !== undefined)
88
+ return 'lte';
89
+ if (condition.contains !== undefined)
90
+ return 'contains';
91
+ if (condition.startsWith !== undefined)
92
+ return 'startsWith';
93
+ if (condition.endsWith !== undefined)
94
+ return 'endsWith';
95
+ if (condition.matches !== undefined)
96
+ return 'matches';
97
+ if (condition.in !== undefined)
98
+ return 'in';
99
+ return 'condition';
100
+ }
101
+ function getExpectedValueLabel(condition) {
102
+ if ('equals' in condition)
103
+ return String(condition.equals);
104
+ if ('not_equals' in condition)
105
+ return String(condition.not_equals);
106
+ if (condition.gt !== undefined)
107
+ return String(condition.gt);
108
+ if (condition.gte !== undefined)
109
+ return String(condition.gte);
110
+ if (condition.lt !== undefined)
111
+ return String(condition.lt);
112
+ if (condition.lte !== undefined)
113
+ return String(condition.lte);
114
+ if (condition.contains !== undefined)
115
+ return condition.contains;
116
+ if (condition.startsWith !== undefined)
117
+ return condition.startsWith;
118
+ if (condition.endsWith !== undefined)
119
+ return condition.endsWith;
120
+ if (condition.matches !== undefined)
121
+ return condition.matches;
122
+ if (condition.in !== undefined)
123
+ return JSON.stringify(condition.in);
124
+ return null;
125
+ }
126
+ function traceStepRunConditionSkipped(stepId, stepTitle, condition, context) {
127
+ return {
128
+ kind: 'evaluated_condition',
129
+ summary: formatConditionTrace(condition, context, false),
130
+ refs: [{ kind: 'step_id', stepId }],
131
+ };
132
+ }
133
+ function traceStepRunConditionPassed(stepId, stepTitle, condition, context) {
134
+ return {
135
+ kind: 'evaluated_condition',
136
+ summary: formatConditionTrace(condition, context, true),
137
+ refs: [{ kind: 'step_id', stepId }],
138
+ };
139
+ }
43
140
  function traceArtifactMatchResult(loopId, iteration, result) {
44
141
  const detail = (() => {
45
142
  switch (result.kind) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.22.0",
3
+ "version": "3.23.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {