@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.
- package/dist/application/services/workflow-service.js +41 -11
- package/dist/cli.js +44 -0
- package/dist/config/feature-flags.d.ts +33 -0
- package/dist/config/feature-flags.js +106 -0
- package/dist/container.d.ts +2 -0
- package/dist/container.js +3 -0
- package/dist/mcp-server.js +31 -6
- package/package.json +1 -1
- package/workflows/CHANGELOG-bug-investigation.md +166 -260
- package/workflows/documentation-update-workflow.json +334 -345
- package/workflows/examples/dashboard-template-workflow.json +176 -0
- package/workflows/systematic-bug-investigation-with-loops.backup-20251106-125543.json +751 -0
- package/workflows/systematic-bug-investigation-with-loops.json +727 -664
- package/web/ADAPTIVE_BACKGROUND_SYSTEM.md +0 -523
- package/web/BACKGROUND_ENHANCEMENTS.md +0 -419
- package/web/COMPONENT_LIBRARY.md +0 -755
- package/web/COMPONENT_MIGRATION_GUIDE.md +0 -537
- package/workflows/dashboard-template-workflow.json +0 -337
- package/workflows/deep-documentation-workflow.json +0 -0
- package/workflows/systemic-bug-investigation-with-loops.json +0 -706
|
@@ -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,
|
|
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
|
+
}
|
package/dist/container.d.ts
CHANGED
|
@@ -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,
|
package/dist/mcp-server.js
CHANGED
|
@@ -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.
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
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
|
-
|
|
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) {
|