@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.
Files changed (131) hide show
  1. package/dist/application/services/enhanced-loop-validator.js +3 -3
  2. package/dist/application/services/step-output-decoder.d.ts +6 -0
  3. package/dist/application/services/step-output-decoder.js +49 -0
  4. package/dist/application/services/validation-engine.d.ts +9 -0
  5. package/dist/application/services/validation-engine.js +142 -18
  6. package/dist/application/services/workflow-interpreter.d.ts +1 -1
  7. package/dist/application/services/workflow-interpreter.js +147 -81
  8. package/dist/application/services/workflow-service.d.ts +2 -0
  9. package/dist/application/services/workflow-service.js +3 -3
  10. package/dist/application/use-cases/validate-step-output.d.ts +2 -0
  11. package/dist/config/feature-flags.js +1 -1
  12. package/dist/di/container.js +88 -0
  13. package/dist/di/tokens.d.ts +16 -0
  14. package/dist/di/tokens.js +16 -0
  15. package/dist/domain/execution/state.d.ts +6 -6
  16. package/dist/domain/workflow-id-policy.d.ts +17 -0
  17. package/dist/domain/workflow-id-policy.js +57 -0
  18. package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.js +33 -6
  19. package/dist/infrastructure/storage/file-workflow-storage.js +3 -1
  20. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +13 -8
  21. package/dist/manifest.json +329 -161
  22. package/dist/mcp/error-mapper.d.ts +3 -8
  23. package/dist/mcp/error-mapper.js +41 -19
  24. package/dist/mcp/handlers/session.js +25 -11
  25. package/dist/mcp/handlers/v2-execution-helpers.d.ts +99 -0
  26. package/dist/mcp/handlers/v2-execution-helpers.js +249 -0
  27. package/dist/mcp/handlers/v2-execution.d.ts +4 -0
  28. package/dist/mcp/handlers/v2-execution.js +1044 -0
  29. package/dist/mcp/handlers/v2-workflow.js +21 -16
  30. package/dist/mcp/handlers/workflow.js +21 -12
  31. package/dist/mcp/index.d.ts +1 -1
  32. package/dist/mcp/index.js +4 -1
  33. package/dist/mcp/output-schemas.d.ts +411 -4
  34. package/dist/mcp/output-schemas.js +57 -1
  35. package/dist/mcp/server.d.ts +1 -1
  36. package/dist/mcp/server.js +96 -65
  37. package/dist/mcp/tool-descriptions.js +32 -15
  38. package/dist/mcp/tools.js +26 -14
  39. package/dist/mcp/types/tool-description-types.d.ts +1 -1
  40. package/dist/mcp/types/tool-description-types.js +7 -5
  41. package/dist/mcp/types.d.ts +40 -3
  42. package/dist/mcp/types.js +32 -3
  43. package/dist/mcp/v2/tool-registry.js +16 -1
  44. package/dist/mcp/v2/tools.d.ts +45 -0
  45. package/dist/mcp/v2/tools.js +21 -1
  46. package/dist/mcp/validation/workflow-next-prevalidate.d.ts +2 -3
  47. package/dist/mcp/validation/workflow-next-prevalidate.js +38 -27
  48. package/dist/v2/durable-core/constants.d.ts +15 -0
  49. package/dist/v2/durable-core/constants.js +18 -0
  50. package/dist/v2/durable-core/domain/ack-advance-append-plan.d.ts +32 -0
  51. package/dist/v2/durable-core/domain/ack-advance-append-plan.js +95 -0
  52. package/dist/v2/durable-core/domain/loop-runtime.d.ts +50 -0
  53. package/dist/v2/durable-core/domain/loop-runtime.js +95 -0
  54. package/dist/v2/durable-core/domain/notes-markdown.d.ts +4 -0
  55. package/dist/v2/durable-core/domain/notes-markdown.js +46 -0
  56. package/dist/v2/durable-core/domain/outputs.d.ts +12 -0
  57. package/dist/v2/durable-core/domain/outputs.js +18 -0
  58. package/dist/v2/durable-core/ids/index.d.ts +2 -0
  59. package/dist/v2/durable-core/ids/index.js +4 -0
  60. package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +100 -6
  61. package/dist/v2/durable-core/schemas/compiled-workflow/index.js +18 -3
  62. package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +113 -113
  63. package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.js +11 -10
  64. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +7129 -0
  65. package/dist/v2/durable-core/schemas/export-bundle/index.js +82 -0
  66. package/dist/v2/durable-core/schemas/lib/decision-trace-ref.d.ts +80 -0
  67. package/dist/v2/durable-core/schemas/lib/decision-trace-ref.js +38 -0
  68. package/dist/v2/durable-core/schemas/lib/dedupe-key.d.ts +8 -0
  69. package/dist/v2/durable-core/schemas/lib/dedupe-key.js +28 -0
  70. package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.d.ts +6 -0
  71. package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.js +12 -0
  72. package/dist/v2/durable-core/schemas/session/events.d.ts +238 -62
  73. package/dist/v2/durable-core/schemas/session/events.js +74 -29
  74. package/dist/v2/durable-core/schemas/session/manifest.d.ts +3 -3
  75. package/dist/v2/durable-core/schemas/session/manifest.js +6 -1
  76. package/dist/v2/durable-core/schemas/session/preferences.d.ts +5 -0
  77. package/dist/v2/durable-core/schemas/session/preferences.js +6 -0
  78. package/dist/v2/durable-core/schemas/session/session-health.d.ts +3 -0
  79. package/dist/v2/durable-core/tokens/index.d.ts +2 -1
  80. package/dist/v2/durable-core/tokens/index.js +4 -4
  81. package/dist/v2/durable-core/tokens/payloads.d.ts +4 -4
  82. package/dist/v2/durable-core/tokens/token-codec.d.ts +3 -2
  83. package/dist/v2/durable-core/tokens/token-codec.js +12 -6
  84. package/dist/v2/durable-core/tokens/token-signer.d.ts +3 -2
  85. package/dist/v2/durable-core/tokens/token-signer.js +8 -9
  86. package/dist/v2/infra/local/base64url/index.d.ts +5 -0
  87. package/dist/v2/infra/local/base64url/index.js +48 -0
  88. package/dist/v2/infra/local/fs/index.js +8 -4
  89. package/dist/v2/infra/local/keyring/index.d.ts +5 -1
  90. package/dist/v2/infra/local/keyring/index.js +41 -32
  91. package/dist/v2/infra/local/pinned-workflow-store/index.d.ts +6 -4
  92. package/dist/v2/infra/local/pinned-workflow-store/index.js +50 -62
  93. package/dist/v2/infra/local/random-entropy/index.d.ts +4 -0
  94. package/dist/v2/infra/local/random-entropy/index.js +10 -0
  95. package/dist/v2/infra/local/session-lock/index.d.ts +3 -1
  96. package/dist/v2/infra/local/session-lock/index.js +5 -4
  97. package/dist/v2/infra/local/session-store/index.d.ts +0 -1
  98. package/dist/v2/infra/local/session-store/index.js +372 -282
  99. package/dist/v2/infra/local/snapshot-store/index.js +20 -25
  100. package/dist/v2/infra/local/time-clock/index.d.ts +5 -0
  101. package/dist/v2/infra/local/time-clock/index.js +12 -0
  102. package/dist/v2/infra/local/utf8/index.d.ts +5 -0
  103. package/dist/v2/infra/local/utf8/index.js +12 -0
  104. package/dist/v2/ports/base64url.port.d.ts +12 -0
  105. package/dist/v2/ports/base64url.port.js +2 -0
  106. package/dist/v2/ports/pinned-workflow-store.port.d.ts +3 -3
  107. package/dist/v2/ports/random-entropy.port.d.ts +3 -0
  108. package/dist/v2/ports/random-entropy.port.js +2 -0
  109. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  110. package/dist/v2/ports/session-lock.port.d.ts +1 -1
  111. package/dist/v2/ports/time-clock.port.d.ts +4 -0
  112. package/dist/v2/ports/time-clock.port.js +2 -0
  113. package/dist/v2/ports/utf8.port.d.ts +3 -0
  114. package/dist/v2/ports/utf8.port.js +2 -0
  115. package/dist/v2/projections/node-outputs.js +28 -11
  116. package/dist/v2/projections/preferences.d.ts +1 -2
  117. package/dist/v2/projections/preferences.js +11 -4
  118. package/dist/v2/projections/run-dag.js +40 -28
  119. package/dist/v2/projections/run-status-signals.d.ts +1 -2
  120. package/dist/v2/read-only/v1-to-v2-shim.d.ts +6 -1
  121. package/dist/v2/read-only/v1-to-v2-shim.js +16 -4
  122. package/dist/v2/usecases/execution-session-gate.d.ts +3 -2
  123. package/dist/v2/usecases/execution-session-gate.js +81 -85
  124. package/package.json +4 -1
  125. package/spec/workflow.schema.json +2 -2
  126. package/workflows/coding-task-workflow-agentic.json +498 -78
  127. package/workflows/design-thinking-workflow-autonomous.agentic.json +1 -1
  128. package/workflows/design-thinking-workflow.json +1 -1
  129. package/workflows/relocation-workflow-us.json +430 -0
  130. package/dist/v2/durable-core/tokens/base64url.d.ts +0 -7
  131. 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
- for (let i = frame.bodyIndex; i < body.length; i++) {
151
- const bodyStep = body[i];
152
- const instance = {
153
- stepId: bodyStep.id,
154
- loopPath: [...state.loopStack.map((f) => ({ loopId: f.loopId, iteration: f.iteration }))],
155
- };
156
- const key = (0, ids_1.toStepInstanceKey)(instance);
157
- if (state.completed.includes(key))
158
- continue;
159
- const projectedContext = this.projectLoopContext(loopCompiled.loop, frame, context);
160
- if (bodyStep.runCondition && !(0, condition_evaluator_1.evaluateCondition)(bodyStep.runCondition, projectedContext)) {
161
- continue;
162
- }
163
- const next = this.materializeStep(compiled, instance, projectedContext);
164
- if (next.isErr())
165
- return (0, neverthrow_1.err)(next.error);
166
- const updatedTop = { ...frame, bodyIndex: i };
167
- const updatedStack = [...state.loopStack.slice(0, -1), updatedTop];
168
- return (0, neverthrow_1.ok)({
169
- state: { ...state, loopStack: updatedStack, pendingStep: instance },
170
- next: next.value,
171
- });
172
- }
173
- if (frame.iteration + 1 > loopCompiled.loop.loop.maxIterations) {
174
- return (0, neverthrow_1.err)(error_1.Err.maxIterationsExceeded(frame.loopId, loopCompiled.loop.loop.maxIterations));
175
- }
176
- const advanced = { ...frame, iteration: frame.iteration + 1, bodyIndex: 0 };
177
- const updatedStack = [...state.loopStack.slice(0, -1), advanced];
178
- return (0, neverthrow_1.ok)({ state: { ...state, loopStack: updatedStack }, next: null });
179
- }
180
- shouldContinueLoop(loop, frame, context) {
181
- if (frame.iteration >= loop.loop.maxIterations) {
182
- return (0, neverthrow_1.ok)(false);
183
- }
184
- switch (loop.loop.type) {
185
- case 'for': {
186
- const count = loop.loop.count;
187
- if (typeof count === 'number') {
188
- return (0, neverthrow_1.ok)(frame.iteration < count);
189
- }
190
- if (typeof count === 'string') {
191
- const raw = context[count];
192
- if (typeof raw !== 'number') {
193
- return (0, neverthrow_1.err)(error_1.Err.missingContext(`for loop '${loop.id}' requires numeric context['${count}']`));
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
- return (0, neverthrow_1.ok)(frame.iteration < raw);
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
- return (0, neverthrow_1.err)(error_1.Err.invalidLoop(loop.id, `for loop '${loop.id}' missing count`));
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
- case 'forEach': {
200
- const itemsVar = loop.loop.items;
201
- if (!itemsVar)
202
- return (0, neverthrow_1.err)(error_1.Err.invalidLoop(loop.id, `forEach loop '${loop.id}' missing items`));
203
- const raw = context[itemsVar];
204
- if (!Array.isArray(raw)) {
205
- return (0, neverthrow_1.err)(error_1.Err.missingContext(`forEach loop '${loop.id}' requires array context['${itemsVar}']`));
206
- }
207
- return (0, neverthrow_1.ok)(frame.iteration < raw.length);
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 'while': {
210
- if (!loop.loop.condition)
211
- return (0, neverthrow_1.err)(error_1.Err.invalidLoop(loop.id, `while loop '${loop.id}' missing condition`));
212
- return (0, neverthrow_1.ok)((0, condition_evaluator_1.evaluateCondition)(loop.loop.condition, this.projectLoopContext(loop, frame, context)));
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 'until': {
215
- if (!loop.loop.condition)
216
- return (0, neverthrow_1.err)(error_1.Err.invalidLoop(loop.id, `until loop '${loop.id}' missing condition`));
217
- return (0, neverthrow_1.ok)(!(0, condition_evaluator_1.evaluateCondition)(loop.loop.condition, this.projectLoopContext(loop, frame, context)));
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.invalidLoop(loop.id, `Unknown loop type '${loop.loop.type}'`));
283
+ return (0, neverthrow_1.err)(error_1.Err.invalidState('Non-exhaustive loop decision'));
221
284
  }
222
285
  }
223
- projectLoopContext(loop, frame, base) {
286
+ projectLoopContextAtIteration(loop, iteration, base) {
224
287
  const out = { ...base };
225
288
  const iterationVar = loop.loop.iterationVar || 'currentIteration';
226
- out[iterationVar] = frame.iteration + 1;
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 = frame.iteration;
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 (Slice 1: list_workflows, inspect_workflow) behind an explicit opt-in flag',
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
  },
@@ -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');
@@ -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
+ }