@exaudeus/workrail 0.6.1-beta.9 → 0.7.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.
@@ -54,8 +54,38 @@ class DefaultWorkflowService {
54
54
  if (enhancedContext._currentLoop) {
55
55
  const { loopId, loopStep } = enhancedContext._currentLoop;
56
56
  const loopContext = new loop_execution_context_1.LoopExecutionContext(loopId, loopStep.loop, enhancedContext._loopState?.[loopId]);
57
+ const bodyStep = this.loopStepResolver.resolveLoopBody(workflow, loopStep.body, loopStep.id);
58
+ const bodyIsCompleted = Array.isArray(bodyStep)
59
+ ? bodyStep.every(step => completed.includes(step.id))
60
+ : completed.includes(bodyStep.id);
61
+ if (bodyIsCompleted) {
62
+ loopContext.incrementIteration();
63
+ if (!enhancedContext._loopState) {
64
+ enhancedContext._loopState = {};
65
+ }
66
+ enhancedContext._loopState[loopId] = loopContext.getCurrentState();
67
+ if (Array.isArray(bodyStep)) {
68
+ bodyStep.forEach(step => {
69
+ const index = completed.indexOf(step.id);
70
+ if (index > -1) {
71
+ completed.splice(index, 1);
72
+ }
73
+ });
74
+ }
75
+ else {
76
+ const index = completed.indexOf(bodyStep.id);
77
+ if (index > -1) {
78
+ completed.splice(index, 1);
79
+ }
80
+ }
81
+ if (!loopContext.shouldContinue(context)) {
82
+ completed.push(loopId);
83
+ delete enhancedContext._currentLoop;
84
+ const nextStep = await this.getNextStep(workflow.id, completed, enhancedContext);
85
+ return nextStep;
86
+ }
87
+ }
57
88
  if (loopContext.shouldContinue(context)) {
58
- const bodyStep = this.loopStepResolver.resolveLoopBody(workflow, loopStep.body, loopStep.id);
59
89
  if (!Array.isArray(bodyStep)) {
60
90
  const isFirst = loopContext.isFirstIteration();
61
91
  if (isFirst && loopContext.isEmpty(context)) {
@@ -83,25 +113,25 @@ class DefaultWorkflowService {
83
113
  };
84
114
  }
85
115
  else {
116
+ const isFirst = loopContext.isFirstIteration();
117
+ if (isFirst && loopContext.isEmpty(context)) {
118
+ const skipContext = context_optimizer_1.ContextOptimizer.createEnhancedContext(context, completed);
119
+ delete skipContext._currentLoop;
120
+ const nextStep = await this.getNextStep(workflow.id, completed, skipContext);
121
+ return nextStep;
122
+ }
123
+ const useMinimal = !isFirst && !!this.loopContextOptimizer;
124
+ const loopEnhancedContext = loopContext.injectVariables(context, useMinimal);
86
125
  const uncompletedBodyStep = bodyStep.find(step => {
87
126
  if (completed.includes(step.id)) {
88
127
  return false;
89
128
  }
90
129
  if (step.runCondition) {
91
- return (0, condition_evaluator_1.evaluateCondition)(step.runCondition, context);
130
+ return (0, condition_evaluator_1.evaluateCondition)(step.runCondition, loopEnhancedContext);
92
131
  }
93
132
  return true;
94
133
  });
95
134
  if (uncompletedBodyStep) {
96
- const isFirst = loopContext.isFirstIteration();
97
- if (isFirst && loopContext.isEmpty(context)) {
98
- const skipContext = context_optimizer_1.ContextOptimizer.createEnhancedContext(context, completed);
99
- delete skipContext._currentLoop;
100
- const nextStep = await this.getNextStep(workflow.id, completed, skipContext);
101
- return nextStep;
102
- }
103
- const useMinimal = !isFirst && !!this.loopContextOptimizer;
104
- const loopEnhancedContext = loopContext.injectVariables(context, useMinimal);
105
135
  const optimizedContext = useMinimal && this.loopContextOptimizer
106
136
  ? this.loopContextOptimizer.stripLoopMetadata(loopEnhancedContext)
107
137
  : loopEnhancedContext;
package/dist/cli.js CHANGED
@@ -1,5 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
38
  };
@@ -178,6 +211,17 @@ async function validateWorkflowFile(filePath) {
178
211
  console.error(chalk_1.default.yellow('\nPlease check the JSON syntax and try again.'));
179
212
  process.exit(1);
180
213
  }
214
+ const { validateWorkflow: schemaValidate } = await Promise.resolve().then(() => __importStar(require('./application/validation')));
215
+ const schemaResult = schemaValidate(workflow);
216
+ if (!schemaResult.valid) {
217
+ console.error(chalk_1.default.red('❌ Workflow validation failed:'), filePath);
218
+ console.error(chalk_1.default.yellow('\nValidation errors:'));
219
+ schemaResult.errors.forEach(error => {
220
+ console.error(chalk_1.default.red(' •'), error);
221
+ });
222
+ console.error(chalk_1.default.yellow('\nPlease fix the errors above and try again.'));
223
+ process.exit(1);
224
+ }
181
225
  const validationEngine = new validation_engine_1.ValidationEngine();
182
226
  const result = validationEngine.validateWorkflow(workflow);
183
227
  if (result.valid && !result.warnings?.length && !result.info?.length) {
@@ -0,0 +1,33 @@
1
+ export interface FeatureFlagDefinition {
2
+ readonly key: string;
3
+ readonly envVar: string;
4
+ readonly defaultValue: boolean;
5
+ readonly description: string;
6
+ readonly since: string;
7
+ readonly stable: boolean;
8
+ }
9
+ export declare const FEATURE_FLAG_DEFINITIONS: ReadonlyArray<FeatureFlagDefinition>;
10
+ export type FeatureFlagKey = typeof FEATURE_FLAG_DEFINITIONS[number]['key'];
11
+ export type FeatureFlags = {
12
+ readonly [K in FeatureFlagKey]: boolean;
13
+ };
14
+ export interface IFeatureFlagProvider {
15
+ isEnabled(key: FeatureFlagKey): boolean;
16
+ getAll(): FeatureFlags;
17
+ getSummary(): string;
18
+ }
19
+ export declare class EnvironmentFeatureFlagProvider implements IFeatureFlagProvider {
20
+ private readonly flags;
21
+ constructor(env?: Record<string, string | undefined>);
22
+ isEnabled(key: FeatureFlagKey): boolean;
23
+ getAll(): FeatureFlags;
24
+ getSummary(): string;
25
+ }
26
+ export declare class StaticFeatureFlagProvider implements IFeatureFlagProvider {
27
+ private readonly flags;
28
+ constructor(flags?: Partial<FeatureFlags>);
29
+ isEnabled(key: FeatureFlagKey): boolean;
30
+ getAll(): FeatureFlags;
31
+ getSummary(): string;
32
+ }
33
+ export declare function createFeatureFlagProvider(env?: Record<string, string | undefined>): IFeatureFlagProvider;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StaticFeatureFlagProvider = exports.EnvironmentFeatureFlagProvider = exports.FEATURE_FLAG_DEFINITIONS = void 0;
4
+ exports.createFeatureFlagProvider = createFeatureFlagProvider;
5
+ exports.FEATURE_FLAG_DEFINITIONS = [
6
+ {
7
+ key: 'sessionTools',
8
+ envVar: 'WORKRAIL_ENABLE_SESSION_TOOLS',
9
+ defaultValue: false,
10
+ description: 'Enable session management tools (workrail_create_session, workrail_update_session, etc.) and HTTP dashboard server',
11
+ since: '0.6.0',
12
+ stable: false,
13
+ },
14
+ {
15
+ key: 'experimentalWorkflows',
16
+ envVar: 'WORKRAIL_ENABLE_EXPERIMENTAL_WORKFLOWS',
17
+ defaultValue: false,
18
+ description: 'Load workflows from experimental/ directory',
19
+ since: '0.6.1',
20
+ stable: false,
21
+ },
22
+ {
23
+ key: 'verboseLogging',
24
+ envVar: 'WORKRAIL_VERBOSE_LOGGING',
25
+ defaultValue: false,
26
+ description: 'Enable detailed debug logging',
27
+ since: '0.6.0',
28
+ stable: true,
29
+ },
30
+ ];
31
+ function parseBoolean(value, defaultValue) {
32
+ if (value === undefined) {
33
+ return defaultValue;
34
+ }
35
+ const normalized = value.toLowerCase().trim();
36
+ const truthyValues = ['true', '1', 'yes', 'on'];
37
+ const falsyValues = ['false', '0', 'no', 'off'];
38
+ if (truthyValues.includes(normalized)) {
39
+ return true;
40
+ }
41
+ if (falsyValues.includes(normalized)) {
42
+ return false;
43
+ }
44
+ console.warn(`[FeatureFlags] Invalid boolean value "${value}" for flag. ` +
45
+ `Expected one of: ${[...truthyValues, ...falsyValues].join(', ')}. ` +
46
+ `Using default: ${defaultValue}`);
47
+ return defaultValue;
48
+ }
49
+ class EnvironmentFeatureFlagProvider {
50
+ constructor(env = process.env) {
51
+ const flags = {};
52
+ for (const definition of exports.FEATURE_FLAG_DEFINITIONS) {
53
+ const envValue = env[definition.envVar];
54
+ flags[definition.key] = parseBoolean(envValue, definition.defaultValue);
55
+ }
56
+ this.flags = flags;
57
+ const enabledExperimental = exports.FEATURE_FLAG_DEFINITIONS
58
+ .filter(def => !def.stable && this.flags[def.key])
59
+ .map(def => def.key);
60
+ if (enabledExperimental.length > 0) {
61
+ console.error(`[FeatureFlags] Experimental features enabled: ${enabledExperimental.join(', ')}`);
62
+ }
63
+ }
64
+ isEnabled(key) {
65
+ return this.flags[key] ?? false;
66
+ }
67
+ getAll() {
68
+ return { ...this.flags };
69
+ }
70
+ getSummary() {
71
+ const lines = ['Feature Flags:'];
72
+ for (const definition of exports.FEATURE_FLAG_DEFINITIONS) {
73
+ const enabled = this.flags[definition.key];
74
+ const status = enabled ? '✓ ENABLED' : '✗ DISABLED';
75
+ const stability = definition.stable ? '[STABLE]' : '[EXPERIMENTAL]';
76
+ lines.push(` ${status} ${stability} ${definition.key}`);
77
+ lines.push(` ${definition.description}`);
78
+ lines.push(` env: ${definition.envVar}`);
79
+ }
80
+ return lines.join('\n');
81
+ }
82
+ }
83
+ exports.EnvironmentFeatureFlagProvider = EnvironmentFeatureFlagProvider;
84
+ class StaticFeatureFlagProvider {
85
+ constructor(flags = {}) {
86
+ this.flags = flags;
87
+ }
88
+ isEnabled(key) {
89
+ return this.flags[key] ?? false;
90
+ }
91
+ getAll() {
92
+ const allFlags = {};
93
+ for (const definition of exports.FEATURE_FLAG_DEFINITIONS) {
94
+ allFlags[definition.key] =
95
+ this.flags[definition.key] ?? definition.defaultValue;
96
+ }
97
+ return allFlags;
98
+ }
99
+ getSummary() {
100
+ return JSON.stringify(this.getAll(), null, 2);
101
+ }
102
+ }
103
+ exports.StaticFeatureFlagProvider = StaticFeatureFlagProvider;
104
+ function createFeatureFlagProvider(env) {
105
+ return new EnvironmentFeatureFlagProvider(env);
106
+ }
@@ -3,7 +3,9 @@ import { ValidationEngine } from './application/services/validation-engine.js';
3
3
  import { IWorkflowStorage } from './types/storage.js';
4
4
  import { WorkflowLookupServer } from './types/server.js';
5
5
  import { ILoopContextOptimizer } from './types/loop-context-optimizer.js';
6
+ import { IFeatureFlagProvider } from './config/feature-flags.js';
6
7
  export interface AppContainer {
8
+ featureFlags: IFeatureFlagProvider;
7
9
  storage: IWorkflowStorage;
8
10
  validationEngine: ValidationEngine;
9
11
  loopContextOptimizer: ILoopContextOptimizer;
package/dist/container.js CHANGED
@@ -6,13 +6,16 @@ const workflow_service_js_1 = require("./application/services/workflow-service.j
6
6
  const validation_engine_js_1 = require("./application/services/validation-engine.js");
7
7
  const server_js_1 = require("./infrastructure/rpc/server.js");
8
8
  const loop_context_optimizer_js_1 = require("./application/services/loop-context-optimizer.js");
9
+ const feature_flags_js_1 = require("./config/feature-flags.js");
9
10
  function createAppContainer(overrides = {}) {
11
+ const featureFlags = overrides.featureFlags ?? (0, feature_flags_js_1.createFeatureFlagProvider)();
10
12
  const storage = overrides.storage ?? (0, storage_js_1.createDefaultWorkflowStorage)();
11
13
  const validationEngine = overrides.validationEngine ?? new validation_engine_js_1.ValidationEngine();
12
14
  const loopContextOptimizer = overrides.loopContextOptimizer ?? new loop_context_optimizer_js_1.LoopContextOptimizer();
13
15
  const workflowService = overrides.workflowService ?? new workflow_service_js_1.DefaultWorkflowService(storage, validationEngine, loopContextOptimizer);
14
16
  const server = overrides.server ?? (0, server_js_1.createWorkflowLookupServer)(workflowService);
15
17
  return {
18
+ featureFlags,
16
19
  storage,
17
20
  validationEngine,
18
21
  loopContextOptimizer,
@@ -38,19 +38,44 @@ const container_js_1 = require("./container.js");
38
38
  const index_js_1 = require("./infrastructure/session/index.js");
39
39
  const session_tools_js_1 = require("./tools/session-tools.js");
40
40
  class WorkflowOrchestrationServer {
41
- constructor() {
42
- this.container = (0, container_js_1.createAppContainer)();
43
- this.sessionManager = new index_js_1.SessionManager();
44
- this.httpServer = new index_js_1.HttpServer(this.sessionManager, { autoOpen: false });
45
- this.sessionTools = (0, session_tools_js_1.createSessionTools)(this.sessionManager, this.httpServer);
41
+ constructor(container) {
42
+ this.sessionManager = null;
43
+ this.httpServer = null;
44
+ this.container = container ?? (0, container_js_1.createAppContainer)();
45
+ this.featureFlags = this.container.featureFlags;
46
+ if (this.featureFlags.isEnabled('sessionTools')) {
47
+ this.sessionManager = new index_js_1.SessionManager();
48
+ this.httpServer = new index_js_1.HttpServer(this.sessionManager, { autoOpen: false });
49
+ this.sessionTools = (0, session_tools_js_1.createSessionTools)(this.sessionManager, this.httpServer);
50
+ console.error('[FeatureFlags] Session tools enabled');
51
+ }
52
+ else {
53
+ this.sessionTools = [];
54
+ console.error('[FeatureFlags] Session tools disabled (enable with WORKRAIL_ENABLE_SESSION_TOOLS=true)');
55
+ }
46
56
  }
47
57
  async initialize() {
48
- await this.httpServer.start();
58
+ if (this.featureFlags.isEnabled('sessionTools') && this.httpServer) {
59
+ await this.httpServer.start();
60
+ console.error('[FeatureFlags] Dashboard server started');
61
+ }
49
62
  }
50
63
  getSessionTools() {
51
64
  return this.sessionTools;
52
65
  }
53
66
  async handleSessionTool(name, args) {
67
+ if (!this.featureFlags.isEnabled('sessionTools') || !this.sessionManager || !this.httpServer) {
68
+ return {
69
+ content: [{
70
+ type: "text",
71
+ text: JSON.stringify({
72
+ error: 'Session tools are not enabled. Set WORKRAIL_ENABLE_SESSION_TOOLS=true to enable.',
73
+ tool: name
74
+ }, null, 2)
75
+ }],
76
+ isError: true
77
+ };
78
+ }
54
79
  return (0, session_tools_js_1.handleSessionTool)(name, args, this.sessionManager, this.httpServer);
55
80
  }
56
81
  async callWorkflowMethod(method, params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "0.6.1-beta.9",
3
+ "version": "0.7.0",
4
4
  "description": "MCP server for structured workflow orchestration and step-by-step task guidance",
5
5
  "license": "MIT",
6
6
  "bin": {