@exaudeus/workrail 0.11.0 → 0.13.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/enhanced-loop-validator.js +3 -3
- package/dist/application/services/step-output-decoder.d.ts +6 -0
- package/dist/application/services/step-output-decoder.js +49 -0
- package/dist/application/services/validation-engine.d.ts +9 -0
- package/dist/application/services/validation-engine.js +142 -18
- package/dist/application/services/workflow-interpreter.d.ts +1 -1
- package/dist/application/services/workflow-interpreter.js +147 -81
- package/dist/application/services/workflow-service.d.ts +2 -0
- package/dist/application/services/workflow-service.js +3 -3
- package/dist/application/use-cases/validate-step-output.d.ts +2 -0
- package/dist/config/feature-flags.js +1 -1
- package/dist/di/container.js +88 -0
- package/dist/di/tokens.d.ts +16 -0
- package/dist/di/tokens.js +16 -0
- package/dist/domain/execution/state.d.ts +6 -6
- package/dist/domain/workflow-id-policy.d.ts +17 -0
- package/dist/domain/workflow-id-policy.js +57 -0
- package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.js +33 -6
- package/dist/infrastructure/storage/file-workflow-storage.js +3 -1
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +13 -8
- package/dist/manifest.json +329 -161
- package/dist/mcp/error-mapper.d.ts +3 -8
- package/dist/mcp/error-mapper.js +41 -19
- package/dist/mcp/handlers/session.js +25 -11
- package/dist/mcp/handlers/v2-execution-helpers.d.ts +99 -0
- package/dist/mcp/handlers/v2-execution-helpers.js +249 -0
- package/dist/mcp/handlers/v2-execution.d.ts +4 -0
- package/dist/mcp/handlers/v2-execution.js +1044 -0
- package/dist/mcp/handlers/v2-workflow.js +21 -16
- package/dist/mcp/handlers/workflow.js +21 -12
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/output-schemas.d.ts +411 -4
- package/dist/mcp/output-schemas.js +57 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +96 -65
- package/dist/mcp/tool-descriptions.js +32 -15
- package/dist/mcp/tools.js +26 -14
- package/dist/mcp/types/tool-description-types.d.ts +1 -1
- package/dist/mcp/types/tool-description-types.js +7 -5
- package/dist/mcp/types.d.ts +40 -3
- package/dist/mcp/types.js +32 -3
- package/dist/mcp/v2/tool-registry.js +16 -1
- package/dist/mcp/v2/tools.d.ts +45 -0
- package/dist/mcp/v2/tools.js +21 -1
- package/dist/mcp/validation/workflow-next-prevalidate.d.ts +2 -3
- package/dist/mcp/validation/workflow-next-prevalidate.js +38 -27
- package/dist/v2/durable-core/constants.d.ts +15 -0
- package/dist/v2/durable-core/constants.js +18 -0
- package/dist/v2/durable-core/domain/ack-advance-append-plan.d.ts +32 -0
- package/dist/v2/durable-core/domain/ack-advance-append-plan.js +95 -0
- package/dist/v2/durable-core/domain/loop-runtime.d.ts +50 -0
- package/dist/v2/durable-core/domain/loop-runtime.js +95 -0
- package/dist/v2/durable-core/domain/notes-markdown.d.ts +4 -0
- package/dist/v2/durable-core/domain/notes-markdown.js +46 -0
- package/dist/v2/durable-core/domain/outputs.d.ts +12 -0
- package/dist/v2/durable-core/domain/outputs.js +18 -0
- package/dist/v2/durable-core/ids/index.d.ts +2 -0
- package/dist/v2/durable-core/ids/index.js +4 -0
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +100 -6
- package/dist/v2/durable-core/schemas/compiled-workflow/index.js +18 -3
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +113 -113
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.js +11 -10
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +7129 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.js +82 -0
- package/dist/v2/durable-core/schemas/lib/decision-trace-ref.d.ts +80 -0
- package/dist/v2/durable-core/schemas/lib/decision-trace-ref.js +38 -0
- package/dist/v2/durable-core/schemas/lib/dedupe-key.d.ts +8 -0
- package/dist/v2/durable-core/schemas/lib/dedupe-key.js +28 -0
- package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.d.ts +6 -0
- package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.js +12 -0
- package/dist/v2/durable-core/schemas/session/events.d.ts +238 -62
- package/dist/v2/durable-core/schemas/session/events.js +74 -29
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +3 -3
- package/dist/v2/durable-core/schemas/session/manifest.js +6 -1
- package/dist/v2/durable-core/schemas/session/preferences.d.ts +5 -0
- package/dist/v2/durable-core/schemas/session/preferences.js +6 -0
- package/dist/v2/durable-core/schemas/session/session-health.d.ts +3 -0
- package/dist/v2/durable-core/tokens/index.d.ts +2 -1
- package/dist/v2/durable-core/tokens/index.js +4 -4
- package/dist/v2/durable-core/tokens/payloads.d.ts +4 -4
- package/dist/v2/durable-core/tokens/token-codec.d.ts +3 -2
- package/dist/v2/durable-core/tokens/token-codec.js +12 -6
- package/dist/v2/durable-core/tokens/token-signer.d.ts +3 -2
- package/dist/v2/durable-core/tokens/token-signer.js +8 -9
- package/dist/v2/infra/local/base64url/index.d.ts +5 -0
- package/dist/v2/infra/local/base64url/index.js +48 -0
- package/dist/v2/infra/local/fs/index.js +8 -4
- package/dist/v2/infra/local/keyring/index.d.ts +5 -1
- package/dist/v2/infra/local/keyring/index.js +41 -32
- package/dist/v2/infra/local/pinned-workflow-store/index.d.ts +6 -4
- package/dist/v2/infra/local/pinned-workflow-store/index.js +50 -62
- package/dist/v2/infra/local/random-entropy/index.d.ts +4 -0
- package/dist/v2/infra/local/random-entropy/index.js +10 -0
- package/dist/v2/infra/local/session-lock/index.d.ts +3 -1
- package/dist/v2/infra/local/session-lock/index.js +5 -4
- package/dist/v2/infra/local/session-store/index.d.ts +0 -1
- package/dist/v2/infra/local/session-store/index.js +372 -282
- package/dist/v2/infra/local/snapshot-store/index.js +20 -25
- package/dist/v2/infra/local/time-clock/index.d.ts +5 -0
- package/dist/v2/infra/local/time-clock/index.js +12 -0
- package/dist/v2/infra/local/utf8/index.d.ts +5 -0
- package/dist/v2/infra/local/utf8/index.js +12 -0
- package/dist/v2/ports/base64url.port.d.ts +12 -0
- package/dist/v2/ports/base64url.port.js +2 -0
- package/dist/v2/ports/pinned-workflow-store.port.d.ts +3 -3
- package/dist/v2/ports/random-entropy.port.d.ts +3 -0
- package/dist/v2/ports/random-entropy.port.js +2 -0
- package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
- package/dist/v2/ports/session-lock.port.d.ts +1 -1
- package/dist/v2/ports/time-clock.port.d.ts +4 -0
- package/dist/v2/ports/time-clock.port.js +2 -0
- package/dist/v2/ports/utf8.port.d.ts +3 -0
- package/dist/v2/ports/utf8.port.js +2 -0
- package/dist/v2/projections/node-outputs.js +28 -11
- package/dist/v2/projections/preferences.d.ts +1 -2
- package/dist/v2/projections/preferences.js +11 -4
- package/dist/v2/projections/run-dag.js +40 -28
- package/dist/v2/projections/run-status-signals.d.ts +1 -2
- package/dist/v2/read-only/v1-to-v2-shim.d.ts +6 -1
- package/dist/v2/read-only/v1-to-v2-shim.js +16 -4
- package/dist/v2/usecases/execution-session-gate.d.ts +3 -2
- package/dist/v2/usecases/execution-session-gate.js +81 -85
- package/package.json +4 -1
- package/spec/workflow.schema.json +2 -2
- package/workflows/coding-task-workflow-agentic.json +498 -78
- package/workflows/design-thinking-workflow-autonomous.agentic.json +1 -1
- package/workflows/design-thinking-workflow.json +1 -1
- package/workflows/relocation-workflow-us.json +430 -0
- package/dist/v2/durable-core/tokens/base64url.d.ts +0 -7
- package/dist/v2/durable-core/tokens/base64url.js +0 -16
|
@@ -13,6 +13,7 @@ const workflow_1 = require("../../types/workflow");
|
|
|
13
13
|
const neverthrow_1 = require("neverthrow");
|
|
14
14
|
const error_1 = require("../../domain/execution/error");
|
|
15
15
|
const ids_1 = require("../../domain/execution/ids");
|
|
16
|
+
const loop_runtime_1 = require("../../v2/durable-core/domain/loop-runtime");
|
|
16
17
|
let WorkflowInterpreter = class WorkflowInterpreter {
|
|
17
18
|
applyEvent(state, event) {
|
|
18
19
|
if (state.kind === 'complete')
|
|
@@ -132,103 +133,165 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
132
133
|
if (!loopCompiled) {
|
|
133
134
|
return (0, neverthrow_1.err)(error_1.Err.invalidLoop(frame.loopId, 'Loop not found in compiled metadata'));
|
|
134
135
|
}
|
|
135
|
-
const shouldContinue = this.shouldContinueLoop(loopCompiled.loop, frame, context);
|
|
136
|
-
if (shouldContinue.isErr())
|
|
137
|
-
return (0, neverthrow_1.err)(shouldContinue.error);
|
|
138
|
-
if (!shouldContinue.value) {
|
|
139
|
-
const popped = state.loopStack.slice(0, -1);
|
|
140
|
-
return (0, neverthrow_1.ok)({
|
|
141
|
-
state: {
|
|
142
|
-
...state,
|
|
143
|
-
loopStack: popped,
|
|
144
|
-
completed: [...state.completed, frame.loopId],
|
|
145
|
-
},
|
|
146
|
-
next: null,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
136
|
const body = loopCompiled.bodySteps;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
137
|
+
const loopPath = [...state.loopStack.map((f) => ({ loopId: f.loopId, iteration: f.iteration }))];
|
|
138
|
+
const completed = new Set(state.completed);
|
|
139
|
+
const ports = {
|
|
140
|
+
shouldEnterIteration: (iteration) => {
|
|
141
|
+
switch (loopCompiled.loop.loop.type) {
|
|
142
|
+
case 'for': {
|
|
143
|
+
const count = loopCompiled.loop.loop.count;
|
|
144
|
+
if (typeof count === 'number') {
|
|
145
|
+
return (0, neverthrow_1.ok)(iteration < count);
|
|
146
|
+
}
|
|
147
|
+
if (typeof count === 'string') {
|
|
148
|
+
const raw = context[count];
|
|
149
|
+
if (typeof raw !== 'number') {
|
|
150
|
+
return (0, neverthrow_1.err)({
|
|
151
|
+
code: 'LOOP_MISSING_CONTEXT',
|
|
152
|
+
loopId: loopCompiled.loop.id,
|
|
153
|
+
message: `for loop '${loopCompiled.loop.id}' requires numeric context['${count}']`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return (0, neverthrow_1.ok)(iteration < raw);
|
|
157
|
+
}
|
|
158
|
+
return (0, neverthrow_1.err)({
|
|
159
|
+
code: 'LOOP_INVALID_CONFIG',
|
|
160
|
+
loopId: loopCompiled.loop.id,
|
|
161
|
+
message: `for loop '${loopCompiled.loop.id}' missing count`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
case 'forEach': {
|
|
165
|
+
const itemsVar = loopCompiled.loop.loop.items;
|
|
166
|
+
if (!itemsVar) {
|
|
167
|
+
return (0, neverthrow_1.err)({
|
|
168
|
+
code: 'LOOP_INVALID_CONFIG',
|
|
169
|
+
loopId: loopCompiled.loop.id,
|
|
170
|
+
message: `forEach loop '${loopCompiled.loop.id}' missing items`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const raw = context[itemsVar];
|
|
174
|
+
if (!Array.isArray(raw)) {
|
|
175
|
+
return (0, neverthrow_1.err)({
|
|
176
|
+
code: 'LOOP_MISSING_CONTEXT',
|
|
177
|
+
loopId: loopCompiled.loop.id,
|
|
178
|
+
message: `forEach loop '${loopCompiled.loop.id}' requires array context['${itemsVar}']`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return (0, neverthrow_1.ok)(iteration < raw.length);
|
|
194
182
|
}
|
|
195
|
-
|
|
183
|
+
case 'while': {
|
|
184
|
+
if (!loopCompiled.loop.loop.condition) {
|
|
185
|
+
return (0, neverthrow_1.err)({
|
|
186
|
+
code: 'LOOP_INVALID_CONFIG',
|
|
187
|
+
loopId: loopCompiled.loop.id,
|
|
188
|
+
message: `while loop '${loopCompiled.loop.id}' missing condition`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return (0, neverthrow_1.ok)((0, condition_evaluator_1.evaluateCondition)(loopCompiled.loop.loop.condition, this.projectLoopContextAtIteration(loopCompiled.loop, iteration, context)));
|
|
192
|
+
}
|
|
193
|
+
case 'until': {
|
|
194
|
+
if (!loopCompiled.loop.loop.condition) {
|
|
195
|
+
return (0, neverthrow_1.err)({
|
|
196
|
+
code: 'LOOP_INVALID_CONFIG',
|
|
197
|
+
loopId: loopCompiled.loop.id,
|
|
198
|
+
message: `until loop '${loopCompiled.loop.id}' missing condition`,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return (0, neverthrow_1.ok)(!(0, condition_evaluator_1.evaluateCondition)(loopCompiled.loop.loop.condition, this.projectLoopContextAtIteration(loopCompiled.loop, iteration, context)));
|
|
202
|
+
}
|
|
203
|
+
default:
|
|
204
|
+
return (0, neverthrow_1.err)({
|
|
205
|
+
code: 'LOOP_INVALID_CONFIG',
|
|
206
|
+
loopId: loopCompiled.loop.id,
|
|
207
|
+
message: `Unknown loop type '${loopCompiled.loop.loop.type}'`,
|
|
208
|
+
});
|
|
196
209
|
}
|
|
197
|
-
|
|
210
|
+
},
|
|
211
|
+
isBodyIndexEligible: (bodyIndex) => {
|
|
212
|
+
const bodyStep = body[bodyIndex];
|
|
213
|
+
if (!bodyStep)
|
|
214
|
+
return false;
|
|
215
|
+
const instance = { stepId: bodyStep.id, loopPath };
|
|
216
|
+
const key = (0, ids_1.toStepInstanceKey)(instance);
|
|
217
|
+
if (completed.has(key))
|
|
218
|
+
return false;
|
|
219
|
+
if (!bodyStep.runCondition)
|
|
220
|
+
return true;
|
|
221
|
+
const projectedContext = this.projectLoopContext(loopCompiled.loop, frame, context);
|
|
222
|
+
return (0, condition_evaluator_1.evaluateCondition)(bodyStep.runCondition, projectedContext);
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
const decision = (0, loop_runtime_1.computeLoopDecision)({
|
|
226
|
+
loopId: frame.loopId,
|
|
227
|
+
iteration: frame.iteration,
|
|
228
|
+
bodyIndex: frame.bodyIndex,
|
|
229
|
+
bodyLength: body.length,
|
|
230
|
+
maxIterations: loopCompiled.loop.loop.maxIterations,
|
|
231
|
+
ports,
|
|
232
|
+
});
|
|
233
|
+
if (decision.isErr()) {
|
|
234
|
+
const e = decision.error;
|
|
235
|
+
switch (e.code) {
|
|
236
|
+
case 'LOOP_MAX_ITERATIONS_REACHED':
|
|
237
|
+
return (0, neverthrow_1.err)(error_1.Err.maxIterationsExceeded(e.loopId, e.maxIterations));
|
|
238
|
+
case 'LOOP_MISSING_CONTEXT':
|
|
239
|
+
return (0, neverthrow_1.err)(error_1.Err.missingContext(e.message));
|
|
240
|
+
case 'LOOP_INVALID_CONFIG':
|
|
241
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidLoop(e.loopId, e.message));
|
|
242
|
+
case 'LOOP_INVALID_STATE':
|
|
243
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState(e.message));
|
|
244
|
+
default:
|
|
245
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState('Unhandled loop kernel error'));
|
|
198
246
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
247
|
+
}
|
|
248
|
+
switch (decision.value.kind) {
|
|
249
|
+
case 'exit_loop': {
|
|
250
|
+
const popped = state.loopStack.slice(0, -1);
|
|
251
|
+
return (0, neverthrow_1.ok)({
|
|
252
|
+
state: {
|
|
253
|
+
...state,
|
|
254
|
+
loopStack: popped,
|
|
255
|
+
completed: [...state.completed, frame.loopId],
|
|
256
|
+
},
|
|
257
|
+
next: null,
|
|
258
|
+
});
|
|
208
259
|
}
|
|
209
|
-
case '
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return (0, neverthrow_1.ok)(
|
|
260
|
+
case 'advance_iteration': {
|
|
261
|
+
const advanced = { ...frame, iteration: decision.value.toIteration, bodyIndex: 0 };
|
|
262
|
+
const updatedStack = [...state.loopStack.slice(0, -1), advanced];
|
|
263
|
+
return (0, neverthrow_1.ok)({ state: { ...state, loopStack: updatedStack }, next: null });
|
|
213
264
|
}
|
|
214
|
-
case '
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
265
|
+
case 'execute_body_step': {
|
|
266
|
+
const bodyStep = body[decision.value.bodyIndex];
|
|
267
|
+
if (!bodyStep) {
|
|
268
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState(`Loop '${frame.loopId}' selected missing bodyIndex ${decision.value.bodyIndex}`));
|
|
269
|
+
}
|
|
270
|
+
const instance = { stepId: bodyStep.id, loopPath };
|
|
271
|
+
const projectedContext = this.projectLoopContext(loopCompiled.loop, frame, context);
|
|
272
|
+
const next = this.materializeStep(compiled, instance, projectedContext);
|
|
273
|
+
if (next.isErr())
|
|
274
|
+
return (0, neverthrow_1.err)(next.error);
|
|
275
|
+
const updatedTop = { ...frame, bodyIndex: decision.value.bodyIndex };
|
|
276
|
+
const updatedStack = [...state.loopStack.slice(0, -1), updatedTop];
|
|
277
|
+
return (0, neverthrow_1.ok)({
|
|
278
|
+
state: { ...state, loopStack: updatedStack, pendingStep: instance },
|
|
279
|
+
next: next.value,
|
|
280
|
+
});
|
|
218
281
|
}
|
|
219
282
|
default:
|
|
220
|
-
return (0, neverthrow_1.err)(error_1.Err.
|
|
283
|
+
return (0, neverthrow_1.err)(error_1.Err.invalidState('Non-exhaustive loop decision'));
|
|
221
284
|
}
|
|
222
285
|
}
|
|
223
|
-
|
|
286
|
+
projectLoopContextAtIteration(loop, iteration, base) {
|
|
224
287
|
const out = { ...base };
|
|
225
288
|
const iterationVar = loop.loop.iterationVar || 'currentIteration';
|
|
226
|
-
out[iterationVar] =
|
|
289
|
+
out[iterationVar] = iteration + 1;
|
|
227
290
|
if (loop.loop.type === 'forEach') {
|
|
228
291
|
const itemsVar = loop.loop.items;
|
|
229
292
|
const raw = base[itemsVar];
|
|
230
293
|
if (Array.isArray(raw)) {
|
|
231
|
-
const index =
|
|
294
|
+
const index = iteration;
|
|
232
295
|
const itemVar = loop.loop.itemVar || 'currentItem';
|
|
233
296
|
const indexVar = loop.loop.indexVar || 'currentIndex';
|
|
234
297
|
out[itemVar] = raw[index];
|
|
@@ -237,6 +300,9 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
237
300
|
}
|
|
238
301
|
return out;
|
|
239
302
|
}
|
|
303
|
+
projectLoopContext(loop, frame, base) {
|
|
304
|
+
return this.projectLoopContextAtIteration(loop, frame.iteration, base);
|
|
305
|
+
}
|
|
240
306
|
lookupStepInstance(compiled, id) {
|
|
241
307
|
const step = compiled.stepById.get(id.stepId);
|
|
242
308
|
if (!step)
|
|
@@ -19,6 +19,7 @@ export interface WorkflowService {
|
|
|
19
19
|
valid: boolean;
|
|
20
20
|
issues: readonly string[];
|
|
21
21
|
suggestions: readonly string[];
|
|
22
|
+
warnings?: readonly string[];
|
|
22
23
|
}>;
|
|
23
24
|
}
|
|
24
25
|
export declare class DefaultWorkflowService implements WorkflowService {
|
|
@@ -41,6 +42,7 @@ export declare class DefaultWorkflowService implements WorkflowService {
|
|
|
41
42
|
valid: boolean;
|
|
42
43
|
issues: readonly string[];
|
|
43
44
|
suggestions: readonly string[];
|
|
45
|
+
warnings?: readonly string[];
|
|
44
46
|
}>;
|
|
45
47
|
private getOrCompile;
|
|
46
48
|
__debugCompiledCacheSize(): number;
|
|
@@ -59,15 +59,15 @@ let DefaultWorkflowService = class DefaultWorkflowService {
|
|
|
59
59
|
async validateStepOutput(workflowId, stepId, output) {
|
|
60
60
|
const workflow = await this.storage.getWorkflowById(workflowId);
|
|
61
61
|
if (!workflow) {
|
|
62
|
-
return { valid: false, issues: [`Workflow '${workflowId}' not found`], suggestions: [] };
|
|
62
|
+
return { valid: false, issues: [`Workflow '${workflowId}' not found`], suggestions: [], warnings: undefined };
|
|
63
63
|
}
|
|
64
64
|
const step = workflow.definition.steps.find((s) => s.id === stepId);
|
|
65
65
|
if (!step) {
|
|
66
|
-
return { valid: false, issues: [`Step '${stepId}' not found in workflow '${workflowId}'`], suggestions: [] };
|
|
66
|
+
return { valid: false, issues: [`Step '${stepId}' not found in workflow '${workflowId}'`], suggestions: [], warnings: undefined };
|
|
67
67
|
}
|
|
68
68
|
const criteria = step.validationCriteria;
|
|
69
69
|
if (!criteria)
|
|
70
|
-
return { valid: true, issues: [], suggestions: [] };
|
|
70
|
+
return { valid: true, issues: [], suggestions: [], warnings: undefined };
|
|
71
71
|
return this.validationEngine.validate(output, criteria);
|
|
72
72
|
}
|
|
73
73
|
getOrCompile(workflowId, workflow) {
|
|
@@ -3,9 +3,11 @@ export declare function createValidateStepOutput(service: WorkflowService): (wor
|
|
|
3
3
|
valid: boolean;
|
|
4
4
|
issues: readonly string[];
|
|
5
5
|
suggestions: readonly string[];
|
|
6
|
+
warnings?: readonly string[];
|
|
6
7
|
}>;
|
|
7
8
|
export declare function validateStepOutput(service: WorkflowService, workflowId: string, stepId: string, output: string): Promise<{
|
|
8
9
|
valid: boolean;
|
|
9
10
|
issues: readonly string[];
|
|
10
11
|
suggestions: readonly string[];
|
|
12
|
+
warnings?: readonly string[];
|
|
11
13
|
}>;
|
|
@@ -57,7 +57,7 @@ exports.FEATURE_FLAG_DEFINITIONS = [
|
|
|
57
57
|
key: 'v2Tools',
|
|
58
58
|
envVar: 'WORKRAIL_ENABLE_V2_TOOLS',
|
|
59
59
|
defaultValue: false,
|
|
60
|
-
description: 'Enable WorkRail v2 MCP tools (
|
|
60
|
+
description: 'Enable WorkRail v2 MCP tools (Slices 1–3: list/inspect/start/continue) behind an explicit opt-in flag',
|
|
61
61
|
since: '0.9.0',
|
|
62
62
|
stable: false,
|
|
63
63
|
},
|
package/dist/di/container.js
CHANGED
|
@@ -172,6 +172,93 @@ async function registerServices() {
|
|
|
172
172
|
tsyringe_1.container.registerSingleton(tokens_js_1.DI.Mcp.DescriptionProvider, ToolDescriptionProvider);
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
+
async function registerV2Services() {
|
|
176
|
+
const { LocalDataDirV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/data-dir/index.js')));
|
|
177
|
+
const { NodeFileSystemV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/fs/index.js')));
|
|
178
|
+
const { NodeSha256V2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/sha256/index.js')));
|
|
179
|
+
const { NodeCryptoV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/crypto/index.js')));
|
|
180
|
+
const { NodeHmacSha256V2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/hmac-sha256/index.js')));
|
|
181
|
+
const { NodeBase64UrlV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/base64url/index.js')));
|
|
182
|
+
const { NodeRandomEntropyV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/random-entropy/index.js')));
|
|
183
|
+
const { NodeTimeClockV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/time-clock/index.js')));
|
|
184
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.DataDir, {
|
|
185
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new LocalDataDirV2(process.env)),
|
|
186
|
+
});
|
|
187
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.FileSystem, {
|
|
188
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeFileSystemV2()),
|
|
189
|
+
});
|
|
190
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.Sha256, {
|
|
191
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeSha256V2()),
|
|
192
|
+
});
|
|
193
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.Crypto, {
|
|
194
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeCryptoV2()),
|
|
195
|
+
});
|
|
196
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.HmacSha256, {
|
|
197
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeHmacSha256V2()),
|
|
198
|
+
});
|
|
199
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.Base64Url, {
|
|
200
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeBase64UrlV2()),
|
|
201
|
+
});
|
|
202
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.RandomEntropy, {
|
|
203
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeRandomEntropyV2()),
|
|
204
|
+
});
|
|
205
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.TimeClock, {
|
|
206
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)(() => new NodeTimeClockV2()),
|
|
207
|
+
});
|
|
208
|
+
const { LocalKeyringV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/keyring/index.js')));
|
|
209
|
+
const { LocalSessionEventLogStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/session-store/index.js')));
|
|
210
|
+
const { LocalSnapshotStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/snapshot-store/index.js')));
|
|
211
|
+
const { LocalPinnedWorkflowStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/pinned-workflow-store/index.js')));
|
|
212
|
+
const { LocalSessionLockV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/session-lock/index.js')));
|
|
213
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.Keyring, {
|
|
214
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
215
|
+
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
216
|
+
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
217
|
+
const base64url = c.resolve(tokens_js_1.DI.V2.Base64Url);
|
|
218
|
+
const entropy = c.resolve(tokens_js_1.DI.V2.RandomEntropy);
|
|
219
|
+
return new LocalKeyringV2(dataDir, fs, base64url, entropy);
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.SessionStore, {
|
|
223
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
224
|
+
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
225
|
+
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
226
|
+
const sha256 = c.resolve(tokens_js_1.DI.V2.Sha256);
|
|
227
|
+
return new LocalSessionEventLogStoreV2(dataDir, fs, sha256);
|
|
228
|
+
}),
|
|
229
|
+
});
|
|
230
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.SnapshotStore, {
|
|
231
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
232
|
+
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
233
|
+
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
234
|
+
const crypto = c.resolve(tokens_js_1.DI.V2.Crypto);
|
|
235
|
+
return new LocalSnapshotStoreV2(dataDir, fs, crypto);
|
|
236
|
+
}),
|
|
237
|
+
});
|
|
238
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.PinnedWorkflowStore, {
|
|
239
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
240
|
+
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
241
|
+
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
242
|
+
return new LocalPinnedWorkflowStoreV2(dataDir, fs);
|
|
243
|
+
}),
|
|
244
|
+
});
|
|
245
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.SessionLock, {
|
|
246
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
247
|
+
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
248
|
+
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
249
|
+
const clock = c.resolve(tokens_js_1.DI.V2.TimeClock);
|
|
250
|
+
return new LocalSessionLockV2(dataDir, fs, clock);
|
|
251
|
+
}),
|
|
252
|
+
});
|
|
253
|
+
const { ExecutionSessionGateV2 } = await Promise.resolve().then(() => __importStar(require('../v2/usecases/execution-session-gate.js')));
|
|
254
|
+
tsyringe_1.container.register(tokens_js_1.DI.V2.ExecutionGate, {
|
|
255
|
+
useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
|
|
256
|
+
const lock = c.resolve(tokens_js_1.DI.V2.SessionLock);
|
|
257
|
+
const store = c.resolve(tokens_js_1.DI.V2.SessionStore);
|
|
258
|
+
return new ExecutionSessionGateV2(lock, store);
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
175
262
|
async function initializeContainer(options = {}) {
|
|
176
263
|
if (initialized)
|
|
177
264
|
return;
|
|
@@ -196,6 +283,7 @@ async function initializeContainer(options = {}) {
|
|
|
196
283
|
registerRuntime(options);
|
|
197
284
|
await registerConfig();
|
|
198
285
|
await registerStorageChain();
|
|
286
|
+
await registerV2Services();
|
|
199
287
|
await registerServices();
|
|
200
288
|
initialized = true;
|
|
201
289
|
console.error('[DI] Container initialized');
|
package/dist/di/tokens.d.ts
CHANGED
|
@@ -21,6 +21,22 @@ export declare const DI: {
|
|
|
21
21
|
readonly Mcp: {
|
|
22
22
|
readonly DescriptionProvider: symbol;
|
|
23
23
|
};
|
|
24
|
+
readonly V2: {
|
|
25
|
+
readonly DataDir: symbol;
|
|
26
|
+
readonly FileSystem: symbol;
|
|
27
|
+
readonly Sha256: symbol;
|
|
28
|
+
readonly Crypto: symbol;
|
|
29
|
+
readonly HmacSha256: symbol;
|
|
30
|
+
readonly Base64Url: symbol;
|
|
31
|
+
readonly RandomEntropy: symbol;
|
|
32
|
+
readonly TimeClock: symbol;
|
|
33
|
+
readonly Keyring: symbol;
|
|
34
|
+
readonly SessionStore: symbol;
|
|
35
|
+
readonly SnapshotStore: symbol;
|
|
36
|
+
readonly PinnedWorkflowStore: symbol;
|
|
37
|
+
readonly SessionLock: symbol;
|
|
38
|
+
readonly ExecutionGate: symbol;
|
|
39
|
+
};
|
|
24
40
|
readonly Runtime: {
|
|
25
41
|
readonly Mode: symbol;
|
|
26
42
|
readonly ProcessLifecyclePolicy: symbol;
|
package/dist/di/tokens.js
CHANGED
|
@@ -24,6 +24,22 @@ exports.DI = {
|
|
|
24
24
|
Mcp: {
|
|
25
25
|
DescriptionProvider: Symbol('Mcp.DescriptionProvider'),
|
|
26
26
|
},
|
|
27
|
+
V2: {
|
|
28
|
+
DataDir: Symbol('V2.DataDir'),
|
|
29
|
+
FileSystem: Symbol('V2.FileSystem'),
|
|
30
|
+
Sha256: Symbol('V2.Sha256'),
|
|
31
|
+
Crypto: Symbol('V2.Crypto'),
|
|
32
|
+
HmacSha256: Symbol('V2.HmacSha256'),
|
|
33
|
+
Base64Url: Symbol('V2.Base64Url'),
|
|
34
|
+
RandomEntropy: Symbol('V2.RandomEntropy'),
|
|
35
|
+
TimeClock: Symbol('V2.TimeClock'),
|
|
36
|
+
Keyring: Symbol('V2.Keyring'),
|
|
37
|
+
SessionStore: Symbol('V2.SessionStore'),
|
|
38
|
+
SnapshotStore: Symbol('V2.SnapshotStore'),
|
|
39
|
+
PinnedWorkflowStore: Symbol('V2.PinnedWorkflowStore'),
|
|
40
|
+
SessionLock: Symbol('V2.SessionLock'),
|
|
41
|
+
ExecutionGate: Symbol('V2.ExecutionGate'),
|
|
42
|
+
},
|
|
27
43
|
Runtime: {
|
|
28
44
|
Mode: Symbol('Runtime.Mode'),
|
|
29
45
|
ProcessLifecyclePolicy: Symbol('Runtime.ProcessLifecyclePolicy'),
|
|
@@ -20,12 +20,12 @@ export declare const LoopFrameSchema: z.ZodObject<{
|
|
|
20
20
|
iteration: z.ZodNumber;
|
|
21
21
|
bodyIndex: z.ZodNumber;
|
|
22
22
|
}, "strip", z.ZodTypeAny, {
|
|
23
|
-
iteration: number;
|
|
24
23
|
loopId: string;
|
|
24
|
+
iteration: number;
|
|
25
25
|
bodyIndex: number;
|
|
26
26
|
}, {
|
|
27
|
-
iteration: number;
|
|
28
27
|
loopId: string;
|
|
28
|
+
iteration: number;
|
|
29
29
|
bodyIndex: number;
|
|
30
30
|
}>;
|
|
31
31
|
export declare const StepInstanceIdSchema: z.ZodObject<{
|
|
@@ -34,23 +34,23 @@ export declare const StepInstanceIdSchema: z.ZodObject<{
|
|
|
34
34
|
loopId: z.ZodString;
|
|
35
35
|
iteration: z.ZodNumber;
|
|
36
36
|
}, "strip", z.ZodTypeAny, {
|
|
37
|
-
iteration: number;
|
|
38
37
|
loopId: string;
|
|
39
|
-
}, {
|
|
40
38
|
iteration: number;
|
|
39
|
+
}, {
|
|
41
40
|
loopId: string;
|
|
41
|
+
iteration: number;
|
|
42
42
|
}>, "many">;
|
|
43
43
|
}, "strip", z.ZodTypeAny, {
|
|
44
44
|
stepId: string;
|
|
45
45
|
loopPath: {
|
|
46
|
-
iteration: number;
|
|
47
46
|
loopId: string;
|
|
47
|
+
iteration: number;
|
|
48
48
|
}[];
|
|
49
49
|
}, {
|
|
50
50
|
stepId: string;
|
|
51
51
|
loopPath: {
|
|
52
|
-
iteration: number;
|
|
53
52
|
loopId: string;
|
|
53
|
+
iteration: number;
|
|
54
54
|
}[];
|
|
55
55
|
}>;
|
|
56
56
|
export declare const ExecutionStateSchema: z.ZodType<ExecutionState>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WorkflowSourceKind } from '../types/workflow-source';
|
|
2
|
+
export type WorkflowIdParse = {
|
|
3
|
+
readonly kind: 'legacy';
|
|
4
|
+
readonly raw: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'namespaced';
|
|
7
|
+
readonly raw: string;
|
|
8
|
+
readonly namespace: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
};
|
|
11
|
+
export interface WorkflowIdLoadValidation {
|
|
12
|
+
readonly parsed: WorkflowIdParse;
|
|
13
|
+
readonly warnings: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function parseWorkflowId(raw: string): WorkflowIdParse | null;
|
|
16
|
+
export declare function validateWorkflowIdForLoad(raw: string, sourceKind: WorkflowSourceKind): WorkflowIdLoadValidation;
|
|
17
|
+
export declare function validateWorkflowIdForSave(raw: string, sourceKind: WorkflowSourceKind): WorkflowIdParse;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseWorkflowId = parseWorkflowId;
|
|
4
|
+
exports.validateWorkflowIdForLoad = validateWorkflowIdForLoad;
|
|
5
|
+
exports.validateWorkflowIdForSave = validateWorkflowIdForSave;
|
|
6
|
+
const error_handler_1 = require("../core/error-handler");
|
|
7
|
+
const NAMESPACED_SEGMENT_RE = /^[a-z][a-z0-9_-]*$/;
|
|
8
|
+
const LEGACY_ID_RE = /^[a-z0-9_-]+$/;
|
|
9
|
+
function parseWorkflowId(raw) {
|
|
10
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
if (!raw.includes('.')) {
|
|
13
|
+
if (!LEGACY_ID_RE.test(raw))
|
|
14
|
+
return null;
|
|
15
|
+
return { kind: 'legacy', raw };
|
|
16
|
+
}
|
|
17
|
+
const parts = raw.split('.');
|
|
18
|
+
if (parts.length !== 2)
|
|
19
|
+
return null;
|
|
20
|
+
const [namespace, name] = parts;
|
|
21
|
+
if (!namespace || !name)
|
|
22
|
+
return null;
|
|
23
|
+
if (!NAMESPACED_SEGMENT_RE.test(namespace))
|
|
24
|
+
return null;
|
|
25
|
+
if (!NAMESPACED_SEGMENT_RE.test(name))
|
|
26
|
+
return null;
|
|
27
|
+
return { kind: 'namespaced', raw, namespace, name };
|
|
28
|
+
}
|
|
29
|
+
function validateWorkflowIdForLoad(raw, sourceKind) {
|
|
30
|
+
const parsed = parseWorkflowId(raw);
|
|
31
|
+
if (!parsed) {
|
|
32
|
+
throw new error_handler_1.InvalidWorkflowError(raw || 'unknown', 'Invalid workflow id format');
|
|
33
|
+
}
|
|
34
|
+
if (parsed.kind === 'legacy') {
|
|
35
|
+
return {
|
|
36
|
+
parsed,
|
|
37
|
+
warnings: ['legacy_workflow_id'],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (parsed.namespace === 'wr' && sourceKind !== 'bundled') {
|
|
41
|
+
throw new error_handler_1.InvalidWorkflowError(parsed.raw, `Reserved workflow namespace 'wr.*' is only allowed for bundled workflows (sourceKind=${sourceKind})`);
|
|
42
|
+
}
|
|
43
|
+
return { parsed, warnings: [] };
|
|
44
|
+
}
|
|
45
|
+
function validateWorkflowIdForSave(raw, sourceKind) {
|
|
46
|
+
const parsed = parseWorkflowId(raw);
|
|
47
|
+
if (!parsed) {
|
|
48
|
+
throw new error_handler_1.InvalidWorkflowError(raw || 'unknown', 'Invalid workflow id format');
|
|
49
|
+
}
|
|
50
|
+
if (parsed.kind === 'legacy') {
|
|
51
|
+
throw new error_handler_1.InvalidWorkflowError(parsed.raw, "Legacy workflow ids (no dot) are no longer allowed for new workflows; use 'namespace.name'");
|
|
52
|
+
}
|
|
53
|
+
if (parsed.namespace === 'wr' && sourceKind !== 'bundled') {
|
|
54
|
+
throw new error_handler_1.InvalidWorkflowError(parsed.raw, `Reserved workflow namespace 'wr.*' is only allowed for bundled workflows (sourceKind=${sourceKind})`);
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|