@exaudeus/workrail 3.2.1 → 3.4.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/compiler/binding-registry.d.ts +3 -0
- package/dist/application/services/compiler/binding-registry.js +71 -0
- package/dist/application/services/compiler/resolve-bindings.d.ts +18 -0
- package/dist/application/services/compiler/resolve-bindings.js +162 -0
- package/dist/application/services/compiler/sentinel-scan.d.ts +9 -0
- package/dist/application/services/compiler/sentinel-scan.js +37 -0
- package/dist/application/services/validation-engine.js +104 -0
- package/dist/application/services/workflow-compiler.d.ts +10 -2
- package/dist/application/services/workflow-compiler.js +25 -6
- package/dist/application/services/workflow-validation-pipeline.js +8 -1
- package/dist/cli.js +2 -2
- package/dist/config/feature-flags.js +4 -3
- package/dist/engine/engine-factory.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/manifest.json +151 -103
- package/dist/mcp/handler-factory.d.ts +1 -1
- package/dist/mcp/handler-factory.js +2 -2
- package/dist/mcp/handlers/v2-checkpoint.js +5 -5
- package/dist/mcp/handlers/v2-error-mapping.js +4 -4
- package/dist/mcp/handlers/v2-execution/continue-advance.js +2 -2
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +76 -60
- package/dist/mcp/handlers/v2-execution/index.js +86 -44
- package/dist/mcp/handlers/v2-execution-helpers.js +1 -1
- package/dist/mcp/handlers/v2-resume.js +10 -5
- package/dist/mcp/handlers/v2-token-ops.d.ts +1 -1
- package/dist/mcp/handlers/v2-token-ops.js +5 -5
- package/dist/mcp/handlers/v2-workspace-resolution.d.ts +1 -0
- package/dist/mcp/handlers/v2-workspace-resolution.js +12 -0
- package/dist/mcp/index.d.ts +4 -1
- package/dist/mcp/index.js +6 -2
- package/dist/mcp/output-schemas.d.ts +148 -8
- package/dist/mcp/output-schemas.js +22 -4
- package/dist/mcp/server.d.ts +6 -4
- package/dist/mcp/server.js +2 -57
- package/dist/mcp/tool-descriptions.js +9 -158
- package/dist/mcp/transports/http-entry.js +6 -25
- package/dist/mcp/transports/shutdown-hooks.d.ts +5 -0
- package/dist/mcp/transports/shutdown-hooks.js +38 -0
- package/dist/mcp/transports/stdio-entry.js +6 -28
- package/dist/mcp/v2/tool-registry.js +2 -1
- package/dist/mcp/v2/tools.d.ts +28 -11
- package/dist/mcp/v2/tools.js +28 -4
- package/dist/mcp/v2-response-formatter.js +28 -1
- package/dist/mcp/validation/suggestion-generator.d.ts +1 -1
- package/dist/mcp/validation/suggestion-generator.js +13 -3
- package/dist/mcp/workflow-protocol-contracts.d.ts +31 -0
- package/dist/mcp/workflow-protocol-contracts.js +207 -0
- package/dist/mcp-server.d.ts +3 -1
- package/dist/mcp-server.js +6 -2
- package/dist/types/workflow-definition.d.ts +7 -0
- package/dist/types/workflow-definition.js +1 -0
- package/dist/v2/durable-core/domain/binding-drift.d.ts +8 -0
- package/dist/v2/durable-core/domain/binding-drift.js +29 -0
- package/dist/v2/durable-core/domain/reason-model.js +2 -2
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +12 -0
- package/dist/v2/durable-core/schemas/compiled-workflow/index.js +2 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +56 -56
- package/dist/v2/durable-core/schemas/session/events.d.ts +16 -16
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +6 -6
- package/dist/v2/projections/resume-ranking.d.ts +1 -0
- package/dist/v2/projections/resume-ranking.js +1 -0
- package/dist/v2/read-only/v1-to-v2-shim.js +27 -10
- package/dist/v2/usecases/resume-session.d.ts +5 -1
- package/dist/v2/usecases/resume-session.js +4 -1
- package/package.json +1 -1
- package/spec/workflow.schema.json +44 -0
- package/workflows/coding-task-workflow-agentic.json +15 -15
- package/workflows/coding-task-workflow-agentic.lean.v2.json +10 -10
- package/workflows/coding-task-workflow-agentic.v2.json +12 -12
- package/workflows/coding-task-workflow-with-loops.json +2 -2
- package/workflows/document-creation-workflow.json +1 -1
- package/workflows/exploration-workflow.json +3 -3
- package/workflows/mr-review-workflow.agentic.v2.json +11 -11
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getProjectBindings = getProjectBindings;
|
|
4
|
+
exports.loadProjectBindings = loadProjectBindings;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const _projectBindingsCache = new Map();
|
|
8
|
+
function getProjectBindings(workflowId, baseDir = process.cwd()) {
|
|
9
|
+
const key = `${workflowId}:${baseDir}`;
|
|
10
|
+
const cached = _projectBindingsCache.get(key);
|
|
11
|
+
if (cached !== undefined)
|
|
12
|
+
return cached;
|
|
13
|
+
const bindings = loadProjectBindings(workflowId, baseDir);
|
|
14
|
+
_projectBindingsCache.set(key, bindings);
|
|
15
|
+
return bindings;
|
|
16
|
+
}
|
|
17
|
+
function loadProjectBindings(workflowId, baseDir = process.cwd()) {
|
|
18
|
+
const filePath = (0, path_1.join)(baseDir, '.workrail', 'bindings.json');
|
|
19
|
+
let raw;
|
|
20
|
+
try {
|
|
21
|
+
raw = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (isNodeError(e) && e.code === 'ENOENT') {
|
|
25
|
+
return new Map();
|
|
26
|
+
}
|
|
27
|
+
console.warn(`[WorkflowCompiler] Failed to read .workrail/bindings.json: ${String(e)}`);
|
|
28
|
+
return new Map();
|
|
29
|
+
}
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
console.warn(`[WorkflowCompiler] .workrail/bindings.json is not valid JSON — ignoring`);
|
|
36
|
+
return new Map();
|
|
37
|
+
}
|
|
38
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
39
|
+
console.warn(`[WorkflowCompiler] .workrail/bindings.json must be a JSON object — ignoring`);
|
|
40
|
+
return new Map();
|
|
41
|
+
}
|
|
42
|
+
const obj = parsed;
|
|
43
|
+
const perWorkflow = obj[workflowId];
|
|
44
|
+
if (perWorkflow !== undefined) {
|
|
45
|
+
if (typeof perWorkflow === 'object' && !Array.isArray(perWorkflow) && perWorkflow !== null) {
|
|
46
|
+
return extractStringMap(perWorkflow, `[${workflowId}]`);
|
|
47
|
+
}
|
|
48
|
+
console.warn(`[WorkflowCompiler] .workrail/bindings.json[${workflowId}] is not an object — ignoring`);
|
|
49
|
+
return new Map();
|
|
50
|
+
}
|
|
51
|
+
const values = Object.values(obj);
|
|
52
|
+
const allStrings = values.every(v => typeof v === 'string');
|
|
53
|
+
if (allStrings && values.length > 0) {
|
|
54
|
+
return extractStringMap(obj, 'root');
|
|
55
|
+
}
|
|
56
|
+
return new Map();
|
|
57
|
+
}
|
|
58
|
+
function extractStringMap(obj, context) {
|
|
59
|
+
const result = new Map();
|
|
60
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
61
|
+
if (typeof value !== 'string') {
|
|
62
|
+
console.warn(`[WorkflowCompiler] .workrail/bindings.json ${context}.${key} is not a string — skipping`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
result.set(key, value);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
function isNodeError(e) {
|
|
70
|
+
return e instanceof Error && 'code' in e;
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Result } from 'neverthrow';
|
|
2
|
+
import type { ExtensionPoint } from '../../../types/workflow-definition.js';
|
|
3
|
+
import type { WorkflowStepDefinition, LoopStepDefinition } from '../../../types/workflow-definition.js';
|
|
4
|
+
import type { ProjectBindings } from './binding-registry.js';
|
|
5
|
+
export declare const BINDING_TOKEN_RE: RegExp;
|
|
6
|
+
export type BindingPassError = {
|
|
7
|
+
readonly code: 'UNKNOWN_BINDING_SLOT';
|
|
8
|
+
readonly stepId: string;
|
|
9
|
+
readonly slotId: string;
|
|
10
|
+
readonly message: string;
|
|
11
|
+
};
|
|
12
|
+
export type BindingSource = 'project_override' | 'default';
|
|
13
|
+
export interface BindingPassResult {
|
|
14
|
+
readonly steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[];
|
|
15
|
+
readonly resolvedBindings: ReadonlyMap<string, string>;
|
|
16
|
+
readonly resolvedOverrides: ReadonlyMap<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveBindingsPass(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[], extensionPoints: readonly ExtensionPoint[], projectBindings: ProjectBindings): Result<BindingPassResult, BindingPassError>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BINDING_TOKEN_RE = void 0;
|
|
4
|
+
exports.resolveBindingsPass = resolveBindingsPass;
|
|
5
|
+
const neverthrow_1 = require("neverthrow");
|
|
6
|
+
const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
|
|
7
|
+
exports.BINDING_TOKEN_RE = /\{\{wr\.bindings\.([^\s{}]+)\}\}/g;
|
|
8
|
+
function resolveSlot(slotId, stepId, extensionPoints, projectBindings) {
|
|
9
|
+
const override = projectBindings.get(slotId);
|
|
10
|
+
if (override !== undefined)
|
|
11
|
+
return (0, neverthrow_1.ok)({ value: override, source: 'project_override' });
|
|
12
|
+
const ep = extensionPoints.find(ep => ep.slotId === slotId);
|
|
13
|
+
if (ep !== undefined)
|
|
14
|
+
return (0, neverthrow_1.ok)({ value: ep.default, source: 'default' });
|
|
15
|
+
const knownSlots = extensionPoints.map(ep => ep.slotId);
|
|
16
|
+
const hint = knownSlots.length === 0
|
|
17
|
+
? 'This workflow declares no extensionPoints. Add an extensionPoints entry with this slotId.'
|
|
18
|
+
: `Declared slots: [${knownSlots.join(', ')}]. Check for typos.`;
|
|
19
|
+
return (0, neverthrow_1.err)({
|
|
20
|
+
code: 'UNKNOWN_BINDING_SLOT',
|
|
21
|
+
stepId,
|
|
22
|
+
slotId,
|
|
23
|
+
message: `Step '${stepId}': unknown binding slot '${slotId}'. ${hint}`,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function resolveBindingTokensInString(text, stepId, extensionPoints, projectBindings) {
|
|
27
|
+
if (!text.includes('{{wr.bindings.'))
|
|
28
|
+
return (0, neverthrow_1.ok)({ text, resolved: new Map() });
|
|
29
|
+
let result = text;
|
|
30
|
+
const resolvedMap = new Map();
|
|
31
|
+
const matches = [];
|
|
32
|
+
const re = new RegExp(exports.BINDING_TOKEN_RE.source, 'g');
|
|
33
|
+
let match;
|
|
34
|
+
while ((match = re.exec(text)) !== null) {
|
|
35
|
+
matches.push({ full: match[0], slotId: match[1] });
|
|
36
|
+
}
|
|
37
|
+
for (const { full, slotId } of matches) {
|
|
38
|
+
const resolved = resolveSlot(slotId, stepId, extensionPoints, projectBindings);
|
|
39
|
+
if (resolved.isErr())
|
|
40
|
+
return (0, neverthrow_1.err)(resolved.error);
|
|
41
|
+
result = result.split(full).join(resolved.value.value);
|
|
42
|
+
resolvedMap.set(slotId, resolved.value);
|
|
43
|
+
}
|
|
44
|
+
return (0, neverthrow_1.ok)({ text: result, resolved: resolvedMap });
|
|
45
|
+
}
|
|
46
|
+
function accumulateSlotResolution(acc, slotId, resolution) {
|
|
47
|
+
acc.all.set(slotId, resolution.value);
|
|
48
|
+
if (resolution.source === 'project_override') {
|
|
49
|
+
acc.overrides.set(slotId, resolution.value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function resolveBindingTokensInPromptValue(value, stepId, extensionPoints, projectBindings, acc) {
|
|
53
|
+
if (typeof value !== 'string')
|
|
54
|
+
return (0, neverthrow_1.ok)(value);
|
|
55
|
+
const res = resolveBindingTokensInString(value, stepId, extensionPoints, projectBindings);
|
|
56
|
+
if (res.isErr())
|
|
57
|
+
return (0, neverthrow_1.err)(res.error);
|
|
58
|
+
for (const [k, resolution] of res.value.resolved)
|
|
59
|
+
accumulateSlotResolution(acc, k, resolution);
|
|
60
|
+
return (0, neverthrow_1.ok)(res.value.text);
|
|
61
|
+
}
|
|
62
|
+
function resolveBindingTokensInPromptValues(values, stepId, extensionPoints, projectBindings, acc) {
|
|
63
|
+
const resolved = [];
|
|
64
|
+
for (const v of values) {
|
|
65
|
+
const res = resolveBindingTokensInPromptValue(v, stepId, extensionPoints, projectBindings, acc);
|
|
66
|
+
if (res.isErr())
|
|
67
|
+
return (0, neverthrow_1.err)(res.error);
|
|
68
|
+
resolved.push(res.value);
|
|
69
|
+
}
|
|
70
|
+
return (0, neverthrow_1.ok)(resolved);
|
|
71
|
+
}
|
|
72
|
+
function resolveBindingTokensInBlocks(blocks, stepId, extensionPoints, projectBindings, acc) {
|
|
73
|
+
let result = { ...blocks };
|
|
74
|
+
if (blocks.goal !== undefined) {
|
|
75
|
+
const res = resolveBindingTokensInPromptValue(blocks.goal, stepId, extensionPoints, projectBindings, acc);
|
|
76
|
+
if (res.isErr())
|
|
77
|
+
return (0, neverthrow_1.err)(res.error);
|
|
78
|
+
result = { ...result, goal: res.value };
|
|
79
|
+
}
|
|
80
|
+
if (blocks.constraints !== undefined) {
|
|
81
|
+
const res = resolveBindingTokensInPromptValues(blocks.constraints, stepId, extensionPoints, projectBindings, acc);
|
|
82
|
+
if (res.isErr())
|
|
83
|
+
return (0, neverthrow_1.err)(res.error);
|
|
84
|
+
result = { ...result, constraints: res.value };
|
|
85
|
+
}
|
|
86
|
+
if (blocks.procedure !== undefined) {
|
|
87
|
+
const res = resolveBindingTokensInPromptValues(blocks.procedure, stepId, extensionPoints, projectBindings, acc);
|
|
88
|
+
if (res.isErr())
|
|
89
|
+
return (0, neverthrow_1.err)(res.error);
|
|
90
|
+
result = { ...result, procedure: res.value };
|
|
91
|
+
}
|
|
92
|
+
if (blocks.verify !== undefined) {
|
|
93
|
+
const res = resolveBindingTokensInPromptValues(blocks.verify, stepId, extensionPoints, projectBindings, acc);
|
|
94
|
+
if (res.isErr())
|
|
95
|
+
return (0, neverthrow_1.err)(res.error);
|
|
96
|
+
result = { ...result, verify: res.value };
|
|
97
|
+
}
|
|
98
|
+
if (blocks.outputRequired !== undefined) {
|
|
99
|
+
const resolvedOutputRequired = {};
|
|
100
|
+
for (const [key, value] of Object.entries(blocks.outputRequired)) {
|
|
101
|
+
const res = resolveBindingTokensInString(value, stepId, extensionPoints, projectBindings);
|
|
102
|
+
if (res.isErr())
|
|
103
|
+
return (0, neverthrow_1.err)(res.error);
|
|
104
|
+
resolvedOutputRequired[key] = res.value.text;
|
|
105
|
+
for (const [k, resolution] of res.value.resolved)
|
|
106
|
+
accumulateSlotResolution(acc, k, resolution);
|
|
107
|
+
}
|
|
108
|
+
result = { ...result, outputRequired: resolvedOutputRequired };
|
|
109
|
+
}
|
|
110
|
+
return (0, neverthrow_1.ok)(result);
|
|
111
|
+
}
|
|
112
|
+
function resolveStepBindings(step, extensionPoints, projectBindings, acc) {
|
|
113
|
+
let updated = step;
|
|
114
|
+
if (step.prompt !== undefined) {
|
|
115
|
+
const res = resolveBindingTokensInString(step.prompt, step.id, extensionPoints, projectBindings);
|
|
116
|
+
if (res.isErr())
|
|
117
|
+
return (0, neverthrow_1.err)(res.error);
|
|
118
|
+
if (res.value.text !== step.prompt) {
|
|
119
|
+
updated = { ...updated, prompt: res.value.text };
|
|
120
|
+
}
|
|
121
|
+
for (const [k, resolution] of res.value.resolved)
|
|
122
|
+
accumulateSlotResolution(acc, k, resolution);
|
|
123
|
+
}
|
|
124
|
+
if (step.promptBlocks !== undefined) {
|
|
125
|
+
const res = resolveBindingTokensInBlocks(step.promptBlocks, step.id, extensionPoints, projectBindings, acc);
|
|
126
|
+
if (res.isErr())
|
|
127
|
+
return (0, neverthrow_1.err)(res.error);
|
|
128
|
+
updated = { ...updated, promptBlocks: res.value };
|
|
129
|
+
}
|
|
130
|
+
return (0, neverthrow_1.ok)(updated);
|
|
131
|
+
}
|
|
132
|
+
function resolveBindingsPass(steps, extensionPoints, projectBindings) {
|
|
133
|
+
const resolved = [];
|
|
134
|
+
const acc = { all: new Map(), overrides: new Map() };
|
|
135
|
+
for (const step of steps) {
|
|
136
|
+
if ((0, workflow_definition_js_1.isLoopStepDefinition)(step)) {
|
|
137
|
+
const loopRes = resolveStepBindings(step, extensionPoints, projectBindings, acc);
|
|
138
|
+
if (loopRes.isErr())
|
|
139
|
+
return (0, neverthrow_1.err)(loopRes.error);
|
|
140
|
+
if (Array.isArray(step.body)) {
|
|
141
|
+
const bodyResolved = [];
|
|
142
|
+
for (const bodyStep of step.body) {
|
|
143
|
+
const bodyRes = resolveStepBindings(bodyStep, extensionPoints, projectBindings, acc);
|
|
144
|
+
if (bodyRes.isErr())
|
|
145
|
+
return (0, neverthrow_1.err)(bodyRes.error);
|
|
146
|
+
bodyResolved.push(bodyRes.value);
|
|
147
|
+
}
|
|
148
|
+
resolved.push({ ...loopRes.value, body: bodyResolved });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
resolved.push(loopRes.value);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const res = resolveStepBindings(step, extensionPoints, projectBindings, acc);
|
|
156
|
+
if (res.isErr())
|
|
157
|
+
return (0, neverthrow_1.err)(res.error);
|
|
158
|
+
resolved.push(res.value);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return (0, neverthrow_1.ok)({ steps: resolved, resolvedBindings: acc.all, resolvedOverrides: acc.overrides });
|
|
162
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Result } from 'neverthrow';
|
|
2
|
+
import type { WorkflowStepDefinition, LoopStepDefinition } from '../../../types/workflow-definition.js';
|
|
3
|
+
export type SentinelError = {
|
|
4
|
+
readonly code: 'UNRESOLVED_TOKEN';
|
|
5
|
+
readonly stepId: string;
|
|
6
|
+
readonly token: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function sentinelScanPass(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[]): Result<void, SentinelError>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sentinelScanPass = sentinelScanPass;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
|
|
6
|
+
const UNRESOLVED_RE = /\{\{wr\./;
|
|
7
|
+
function sentinelScanPass(steps) {
|
|
8
|
+
for (const step of steps) {
|
|
9
|
+
if (step.prompt && UNRESOLVED_RE.test(step.prompt)) {
|
|
10
|
+
const match = step.prompt.match(/\{\{wr\.[^}]*\}\}/);
|
|
11
|
+
const token = match ? match[0] : '{{wr.*}}';
|
|
12
|
+
return (0, neverthrow_1.err)({
|
|
13
|
+
code: 'UNRESOLVED_TOKEN',
|
|
14
|
+
stepId: step.id,
|
|
15
|
+
token,
|
|
16
|
+
message: `Step '${step.id}': compiled prompt contains unresolved token '${token}'. ` +
|
|
17
|
+
`This indicates a bug in an upstream compiler pass.`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if ((0, workflow_definition_js_1.isLoopStepDefinition)(step) && Array.isArray(step.body)) {
|
|
21
|
+
for (const bodyStep of step.body) {
|
|
22
|
+
if (bodyStep.prompt && UNRESOLVED_RE.test(bodyStep.prompt)) {
|
|
23
|
+
const match = bodyStep.prompt.match(/\{\{wr\.[^}]*\}\}/);
|
|
24
|
+
const token = match ? match[0] : '{{wr.*}}';
|
|
25
|
+
return (0, neverthrow_1.err)({
|
|
26
|
+
code: 'UNRESOLVED_TOKEN',
|
|
27
|
+
stepId: bodyStep.id,
|
|
28
|
+
token,
|
|
29
|
+
message: `Loop body step '${bodyStep.id}': compiled prompt contains unresolved token '${token}'. ` +
|
|
30
|
+
`This indicates a bug in an upstream compiler pass.`,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return (0, neverthrow_1.ok)(undefined);
|
|
37
|
+
}
|
|
@@ -22,6 +22,7 @@ const condition_evaluator_1 = require("../../utils/condition-evaluator");
|
|
|
22
22
|
const ajv_1 = __importDefault(require("ajv"));
|
|
23
23
|
const neverthrow_1 = require("neverthrow");
|
|
24
24
|
const workflow_definition_1 = require("../../types/workflow-definition");
|
|
25
|
+
const resolve_bindings_1 = require("./compiler/resolve-bindings");
|
|
25
26
|
const enhanced_loop_validator_1 = require("./enhanced-loop-validator");
|
|
26
27
|
const step_output_decoder_1 = require("./step-output-decoder");
|
|
27
28
|
let ValidationEngine = ValidationEngine_1 = class ValidationEngine {
|
|
@@ -458,6 +459,109 @@ let ValidationEngine = ValidationEngine_1 = class ValidationEngine {
|
|
|
458
459
|
const suggestions = [];
|
|
459
460
|
const warnings = [];
|
|
460
461
|
const info = [];
|
|
462
|
+
const extensionPoints = workflow.definition.extensionPoints ?? [];
|
|
463
|
+
if (extensionPoints.length > 0) {
|
|
464
|
+
const seenSlotIds = new Set();
|
|
465
|
+
for (const ep of extensionPoints) {
|
|
466
|
+
if (!ep.slotId || typeof ep.slotId !== 'string') {
|
|
467
|
+
issues.push(`extensionPoints entry has missing or empty slotId`);
|
|
468
|
+
suggestions.push('Each extensionPoint must have a non-empty slotId string');
|
|
469
|
+
}
|
|
470
|
+
else if (!ep.purpose || typeof ep.purpose !== 'string') {
|
|
471
|
+
issues.push(`extensionPoints[${ep.slotId}]: purpose must be a non-empty string`);
|
|
472
|
+
}
|
|
473
|
+
else if (!ep.default || typeof ep.default !== 'string') {
|
|
474
|
+
issues.push(`extensionPoints[${ep.slotId}]: default must be a non-empty string`);
|
|
475
|
+
}
|
|
476
|
+
if (ep.slotId) {
|
|
477
|
+
if (seenSlotIds.has(ep.slotId)) {
|
|
478
|
+
issues.push(`extensionPoints has duplicate slotId '${ep.slotId}'`);
|
|
479
|
+
suggestions.push('Each slotId must be unique within extensionPoints');
|
|
480
|
+
}
|
|
481
|
+
seenSlotIds.add(ep.slotId);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const declaredSlotIds = new Set(extensionPoints.map(ep => ep.slotId).filter(Boolean));
|
|
485
|
+
const checkStringForUnknownBindings = (text, stepId) => {
|
|
486
|
+
resolve_bindings_1.BINDING_TOKEN_RE.lastIndex = 0;
|
|
487
|
+
let match;
|
|
488
|
+
while ((match = resolve_bindings_1.BINDING_TOKEN_RE.exec(text)) !== null) {
|
|
489
|
+
const slotId = match[1];
|
|
490
|
+
if (!declaredSlotIds.has(slotId)) {
|
|
491
|
+
issues.push(`Step '${stepId}': binding token '{{wr.bindings.${slotId}}}' references undeclared slot. Declared slots: [${[...declaredSlotIds].join(', ')}]`);
|
|
492
|
+
suggestions.push(`Add an extensionPoint with slotId '${slotId}' to the workflow definition`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
const checkStepForUnknownBindings = (step, stepId) => {
|
|
497
|
+
if (step.prompt !== undefined) {
|
|
498
|
+
checkStringForUnknownBindings(step.prompt, stepId);
|
|
499
|
+
}
|
|
500
|
+
if (step.promptBlocks !== undefined) {
|
|
501
|
+
const pb = step.promptBlocks;
|
|
502
|
+
if (typeof pb.goal === 'string')
|
|
503
|
+
checkStringForUnknownBindings(pb.goal, stepId);
|
|
504
|
+
for (const v of pb.constraints ?? []) {
|
|
505
|
+
if (typeof v === 'string')
|
|
506
|
+
checkStringForUnknownBindings(v, stepId);
|
|
507
|
+
}
|
|
508
|
+
for (const v of pb.procedure ?? []) {
|
|
509
|
+
if (typeof v === 'string')
|
|
510
|
+
checkStringForUnknownBindings(v, stepId);
|
|
511
|
+
}
|
|
512
|
+
for (const v of pb.verify ?? []) {
|
|
513
|
+
if (typeof v === 'string')
|
|
514
|
+
checkStringForUnknownBindings(v, stepId);
|
|
515
|
+
}
|
|
516
|
+
for (const v of Object.values(pb.outputRequired ?? {})) {
|
|
517
|
+
checkStringForUnknownBindings(v, stepId);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
for (const step of workflow.definition.steps) {
|
|
522
|
+
checkStepForUnknownBindings(step, step.id);
|
|
523
|
+
if ((0, workflow_definition_1.isLoopStepDefinition)(step) && Array.isArray(step.body)) {
|
|
524
|
+
for (const inlineStep of step.body) {
|
|
525
|
+
checkStepForUnknownBindings(inlineStep, inlineStep.id ?? 'unknown');
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
const BINDING_TOKEN_DETECT = /\{\{wr\.bindings\./;
|
|
532
|
+
const stepUsesBindingToken = (step) => {
|
|
533
|
+
if (typeof step.prompt === 'string' && BINDING_TOKEN_DETECT.test(step.prompt))
|
|
534
|
+
return true;
|
|
535
|
+
if (step.promptBlocks !== undefined) {
|
|
536
|
+
const pb = step.promptBlocks;
|
|
537
|
+
if (typeof pb.goal === 'string' && BINDING_TOKEN_DETECT.test(pb.goal))
|
|
538
|
+
return true;
|
|
539
|
+
for (const v of [...(pb.constraints ?? []), ...(pb.procedure ?? []), ...(pb.verify ?? [])]) {
|
|
540
|
+
if (typeof v === 'string' && BINDING_TOKEN_DETECT.test(v))
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
for (const v of Object.values(pb.outputRequired ?? {})) {
|
|
544
|
+
if (BINDING_TOKEN_DETECT.test(v))
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return false;
|
|
549
|
+
};
|
|
550
|
+
for (const step of workflow.definition.steps) {
|
|
551
|
+
if (stepUsesBindingToken(step)) {
|
|
552
|
+
issues.push(`Step '${step.id}': uses {{wr.bindings.*}} token but workflow declares no extensionPoints`);
|
|
553
|
+
suggestions.push('Add an extensionPoints array to the workflow definition');
|
|
554
|
+
}
|
|
555
|
+
if ((0, workflow_definition_1.isLoopStepDefinition)(step) && Array.isArray(step.body)) {
|
|
556
|
+
for (const inlineStep of step.body) {
|
|
557
|
+
if (stepUsesBindingToken(inlineStep)) {
|
|
558
|
+
issues.push(`Step '${inlineStep.id ?? 'unknown'}' (loop body): uses {{wr.bindings.*}} token but workflow declares no extensionPoints`);
|
|
559
|
+
suggestions.push('Add an extensionPoints array to the workflow definition');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
461
565
|
const stepIds = new Set();
|
|
462
566
|
for (const step of workflow.definition.steps) {
|
|
463
567
|
if (stepIds.has(step.id)) {
|
|
@@ -2,6 +2,7 @@ import { Workflow, WorkflowStepDefinition, LoopStepDefinition } from '../../type
|
|
|
2
2
|
import type { LoopConditionSource } from '../../types/workflow-definition';
|
|
3
3
|
import type { Result } from 'neverthrow';
|
|
4
4
|
import { type DomainError } from '../../domain/execution/error';
|
|
5
|
+
import type { ExtensionPoint } from '../../types/workflow-definition';
|
|
5
6
|
export interface CompiledLoop {
|
|
6
7
|
readonly loop: LoopStepDefinition;
|
|
7
8
|
readonly bodySteps: readonly WorkflowStepDefinition[];
|
|
@@ -13,10 +14,17 @@ export interface CompiledWorkflow {
|
|
|
13
14
|
readonly stepById: ReadonlyMap<string, WorkflowStepDefinition | LoopStepDefinition>;
|
|
14
15
|
readonly compiledLoops: ReadonlyMap<string, CompiledLoop>;
|
|
15
16
|
readonly loopBodyStepIds: ReadonlySet<string>;
|
|
17
|
+
readonly resolvedBindings: ReadonlyMap<string, string>;
|
|
18
|
+
readonly resolvedOverrides: ReadonlyMap<string, string>;
|
|
16
19
|
}
|
|
17
|
-
export
|
|
20
|
+
export interface ResolvedDefinitionResult {
|
|
21
|
+
readonly steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[];
|
|
22
|
+
readonly resolvedBindings: ReadonlyMap<string, string>;
|
|
23
|
+
readonly resolvedOverrides: ReadonlyMap<string, string>;
|
|
24
|
+
}
|
|
25
|
+
export declare function resolveDefinitionSteps(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[], features: readonly string[], extensionPoints?: readonly ExtensionPoint[], workflowId?: string, baseDir?: string): Result<ResolvedDefinitionResult, DomainError>;
|
|
18
26
|
export declare class WorkflowCompiler {
|
|
19
|
-
compile(workflow: Workflow): Result<CompiledWorkflow, DomainError>;
|
|
27
|
+
compile(workflow: Workflow, baseDir?: string): Result<CompiledWorkflow, DomainError>;
|
|
20
28
|
private deriveConditionSource;
|
|
21
29
|
private resolveLoopBody;
|
|
22
30
|
}
|
|
@@ -21,6 +21,9 @@ const feature_registry_1 = require("./compiler/feature-registry");
|
|
|
21
21
|
const resolve_templates_1 = require("./compiler/resolve-templates");
|
|
22
22
|
const template_registry_1 = require("./compiler/template-registry");
|
|
23
23
|
const routine_loader_1 = require("./compiler/routine-loader");
|
|
24
|
+
const resolve_bindings_1 = require("./compiler/resolve-bindings");
|
|
25
|
+
const binding_registry_1 = require("./compiler/binding-registry");
|
|
26
|
+
const sentinel_scan_1 = require("./compiler/sentinel-scan");
|
|
24
27
|
const _refRegistry = (0, ref_registry_1.createRefRegistry)();
|
|
25
28
|
const _featureRegistry = (0, feature_registry_1.createFeatureRegistry)();
|
|
26
29
|
function buildTemplateRegistry() {
|
|
@@ -52,7 +55,7 @@ function getTemplateRegistry() {
|
|
|
52
55
|
}
|
|
53
56
|
return _templateRegistryCache;
|
|
54
57
|
}
|
|
55
|
-
function resolveDefinitionSteps(steps, features) {
|
|
58
|
+
function resolveDefinitionSteps(steps, features, extensionPoints = [], workflowId = '', baseDir) {
|
|
56
59
|
const templatesResult = (0, resolve_templates_1.resolveTemplatesPass)(steps, getTemplateRegistry());
|
|
57
60
|
if (templatesResult.isErr()) {
|
|
58
61
|
const e = templatesResult.error;
|
|
@@ -63,7 +66,12 @@ function resolveDefinitionSteps(steps, features) {
|
|
|
63
66
|
: `Step '${e.stepId}': template expansion error — ${e.cause.message}`;
|
|
64
67
|
return (0, neverthrow_1.err)(error_1.Err.invalidState(message));
|
|
65
68
|
}
|
|
66
|
-
const
|
|
69
|
+
const bindingsResult = (0, resolve_bindings_1.resolveBindingsPass)(templatesResult.value, extensionPoints, workflowId ? (0, binding_registry_1.getProjectBindings)(workflowId, baseDir) : new Map());
|
|
70
|
+
if (bindingsResult.isErr()) {
|
|
71
|
+
const e = bindingsResult.error;
|
|
72
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState(e.message));
|
|
73
|
+
}
|
|
74
|
+
const featuresResult = (0, resolve_features_1.resolveFeaturesPass)(bindingsResult.value.steps, features, _featureRegistry);
|
|
67
75
|
if (featuresResult.isErr()) {
|
|
68
76
|
const e = featuresResult.error;
|
|
69
77
|
const message = e.code === 'FEATURE_RESOLVE_ERROR'
|
|
@@ -84,14 +92,23 @@ function resolveDefinitionSteps(steps, features) {
|
|
|
84
92
|
: `Step '${e.stepId}': promptBlocks error — ${e.cause.message}`;
|
|
85
93
|
return (0, neverthrow_1.err)(error_1.Err.invalidState(message));
|
|
86
94
|
}
|
|
87
|
-
|
|
95
|
+
const sentinelResult = (0, sentinel_scan_1.sentinelScanPass)(blocksResult.value);
|
|
96
|
+
if (sentinelResult.isErr()) {
|
|
97
|
+
const e = sentinelResult.error;
|
|
98
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState(e.message));
|
|
99
|
+
}
|
|
100
|
+
return (0, neverthrow_1.ok)({
|
|
101
|
+
steps: blocksResult.value,
|
|
102
|
+
resolvedBindings: bindingsResult.value.resolvedBindings,
|
|
103
|
+
resolvedOverrides: bindingsResult.value.resolvedOverrides,
|
|
104
|
+
});
|
|
88
105
|
}
|
|
89
106
|
let WorkflowCompiler = class WorkflowCompiler {
|
|
90
|
-
compile(workflow) {
|
|
91
|
-
const resolvedResult = resolveDefinitionSteps(workflow.definition.steps, workflow.definition.features ?? []);
|
|
107
|
+
compile(workflow, baseDir) {
|
|
108
|
+
const resolvedResult = resolveDefinitionSteps(workflow.definition.steps, workflow.definition.features ?? [], workflow.definition.extensionPoints ?? [], workflow.definition.id, baseDir);
|
|
92
109
|
if (resolvedResult.isErr())
|
|
93
110
|
return (0, neverthrow_1.err)(resolvedResult.error);
|
|
94
|
-
const steps = resolvedResult.value;
|
|
111
|
+
const { steps, resolvedBindings, resolvedOverrides } = resolvedResult.value;
|
|
95
112
|
const stepById = new Map();
|
|
96
113
|
for (const step of steps) {
|
|
97
114
|
if (stepById.has(step.id)) {
|
|
@@ -136,6 +153,8 @@ let WorkflowCompiler = class WorkflowCompiler {
|
|
|
136
153
|
stepById,
|
|
137
154
|
compiledLoops,
|
|
138
155
|
loopBodyStepIds,
|
|
156
|
+
resolvedBindings,
|
|
157
|
+
resolvedOverrides,
|
|
139
158
|
});
|
|
140
159
|
}
|
|
141
160
|
deriveConditionSource(loop, bodySteps) {
|
|
@@ -24,7 +24,14 @@ function validateWorkflowPhase1a(workflow, deps) {
|
|
|
24
24
|
if (normalizationResult.isErr()) {
|
|
25
25
|
return { kind: 'normalization_failed', workflowId, cause: normalizationResult.error };
|
|
26
26
|
}
|
|
27
|
-
const
|
|
27
|
+
const { resolvedBindings, resolvedOverrides } = v1CompilationResult.value;
|
|
28
|
+
const snapshotBase = normalizationResult.value;
|
|
29
|
+
const snapshotWithBindings = resolvedBindings.size > 0
|
|
30
|
+
? { ...snapshotBase, resolvedBindings: Object.fromEntries(resolvedBindings) }
|
|
31
|
+
: snapshotBase;
|
|
32
|
+
const snapshot = resolvedOverrides.size > 0
|
|
33
|
+
? { ...snapshotWithBindings, pinnedOverrides: Object.fromEntries(resolvedOverrides) }
|
|
34
|
+
: snapshotWithBindings;
|
|
28
35
|
if ((0, workflow_definition_js_1.hasWorkflowDefinitionShape)(snapshot.definition)) {
|
|
29
36
|
const executableWorkflow = (0, workflow_js_1.createWorkflow)(snapshot.definition, (0, workflow_source_js_1.createBundledSource)());
|
|
30
37
|
const execCompileResult = deps.compiler.compile(executableWorkflow);
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const os_1 = __importDefault(require("os"));
|
|
11
11
|
const container_js_1 = require("./di/container.js");
|
|
12
12
|
const tokens_js_1 = require("./di/tokens.js");
|
|
13
|
-
const
|
|
13
|
+
const stdio_entry_js_1 = require("./mcp/transports/stdio-entry.js");
|
|
14
14
|
const workflow_js_1 = require("./types/workflow.js");
|
|
15
15
|
const validation_js_1 = require("./application/validation.js");
|
|
16
16
|
const validate_workflow_file_js_1 = require("./application/use-cases/validate-workflow-file.js");
|
|
@@ -116,7 +116,7 @@ program
|
|
|
116
116
|
await (0, container_js_1.initializeContainer)({ runtimeMode: { kind: 'cli' } });
|
|
117
117
|
const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
|
|
118
118
|
const result = await (0, index_js_1.executeStartCommand)({
|
|
119
|
-
createServer: () => ({ start: () => (0,
|
|
119
|
+
createServer: () => ({ start: () => (0, stdio_entry_js_1.startStdioServer)() }),
|
|
120
120
|
});
|
|
121
121
|
(0, interpret_result_js_1.interpretCliResult)(result, terminator);
|
|
122
122
|
});
|
|
@@ -40,10 +40,10 @@ exports.FEATURE_FLAG_DEFINITIONS = [
|
|
|
40
40
|
{
|
|
41
41
|
key: 'agenticRoutines',
|
|
42
42
|
envVar: 'WORKRAIL_ENABLE_AGENTIC_ROUTINES',
|
|
43
|
-
defaultValue:
|
|
43
|
+
defaultValue: true,
|
|
44
44
|
description: 'Enable Agentic Orchestration features (subagent delegation, .agentic.json overrides, routines)',
|
|
45
45
|
since: '0.8.3',
|
|
46
|
-
stable:
|
|
46
|
+
stable: true,
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
key: 'leanWorkflows',
|
|
@@ -162,7 +162,8 @@ class StaticFeatureFlagProvider {
|
|
|
162
162
|
this.flags = flags;
|
|
163
163
|
}
|
|
164
164
|
isEnabled(key) {
|
|
165
|
-
|
|
165
|
+
const definition = exports.FEATURE_FLAG_DEFINITIONS.find(flag => flag.key === key);
|
|
166
|
+
return this.flags[key] ?? definition?.defaultValue ?? false;
|
|
166
167
|
}
|
|
167
168
|
getAll() {
|
|
168
169
|
const allFlags = {};
|
|
@@ -270,7 +270,7 @@ async function createWorkRailEngine(config = {}) {
|
|
|
270
270
|
}
|
|
271
271
|
return (0, types_js_1.engineOk)({
|
|
272
272
|
checkpointNodeId: result.value.checkpointNodeId,
|
|
273
|
-
stateToken: (0, types_js_1.asStateToken)(result.value.
|
|
273
|
+
stateToken: (0, types_js_1.asStateToken)(result.value.resumeToken),
|
|
274
274
|
});
|
|
275
275
|
},
|
|
276
276
|
async listWorkflows() {
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ export { DI } from './di/tokens.js';
|
|
|
4
4
|
export type { WorkflowService } from './application/services/workflow-service.js';
|
|
5
5
|
export type { IWorkflowStorage } from './types/storage.js';
|
|
6
6
|
export type { IFeatureFlagProvider } from './config/feature-flags.js';
|
|
7
|
-
export {
|
|
7
|
+
export { composeServer } from './mcp/server.js';
|
|
8
|
+
export { startStdioServer } from './mcp/transports/stdio-entry.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.
|
|
4
|
+
exports.startStdioServer = exports.composeServer = exports.DI = exports.resetContainer = exports.container = exports.initializeContainer = exports.bootstrap = void 0;
|
|
5
5
|
var container_js_1 = require("./di/container.js");
|
|
6
6
|
Object.defineProperty(exports, "bootstrap", { enumerable: true, get: function () { return container_js_1.bootstrap; } });
|
|
7
7
|
Object.defineProperty(exports, "initializeContainer", { enumerable: true, get: function () { return container_js_1.initializeContainer; } });
|
|
@@ -10,4 +10,6 @@ Object.defineProperty(exports, "resetContainer", { enumerable: true, get: functi
|
|
|
10
10
|
var tokens_js_1 = require("./di/tokens.js");
|
|
11
11
|
Object.defineProperty(exports, "DI", { enumerable: true, get: function () { return tokens_js_1.DI; } });
|
|
12
12
|
var server_js_1 = require("./mcp/server.js");
|
|
13
|
-
Object.defineProperty(exports, "
|
|
13
|
+
Object.defineProperty(exports, "composeServer", { enumerable: true, get: function () { return server_js_1.composeServer; } });
|
|
14
|
+
var stdio_entry_js_1 = require("./mcp/transports/stdio-entry.js");
|
|
15
|
+
Object.defineProperty(exports, "startStdioServer", { enumerable: true, get: function () { return stdio_entry_js_1.startStdioServer; } });
|