@exaudeus/workrail 3.13.0 → 3.15.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 (62) hide show
  1. package/dist/application/services/validation-engine.js +4 -9
  2. package/dist/application/services/workflow-compiler.js +4 -6
  3. package/dist/console/assets/index-BZYIjrzJ.js +28 -0
  4. package/dist/console/assets/index-OLCKbDdm.css +1 -0
  5. package/dist/console/index.html +2 -2
  6. package/dist/engine/engine-factory.js +2 -2
  7. package/dist/engine/types.d.ts +1 -1
  8. package/dist/manifest.json +63 -63
  9. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +5 -0
  10. package/dist/mcp/handlers/shared/request-workflow-reader.js +47 -2
  11. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +1 -1
  12. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +4 -5
  13. package/dist/mcp/handlers/v2-advance-core/index.js +1 -1
  14. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +1 -1
  15. package/dist/mcp/handlers/v2-execution/start.d.ts +1 -0
  16. package/dist/mcp/handlers/v2-execution/start.js +20 -1
  17. package/dist/mcp/handlers/v2-workflow.d.ts +23 -0
  18. package/dist/mcp/handlers/v2-workflow.js +177 -10
  19. package/dist/mcp/output-schemas.d.ts +202 -8
  20. package/dist/mcp/output-schemas.js +38 -11
  21. package/dist/mcp/server.js +48 -1
  22. package/dist/mcp/tool-descriptions.js +17 -9
  23. package/dist/mcp/v2/tools.d.ts +6 -0
  24. package/dist/mcp/v2/tools.js +2 -0
  25. package/dist/mcp/workflow-protocol-contracts.js +5 -1
  26. package/dist/types/workflow-definition.d.ts +2 -2
  27. package/dist/v2/infra/local/workspace-anchor/index.js +4 -1
  28. package/dist/v2/usecases/console-routes.js +49 -1
  29. package/dist/v2/usecases/console-service.d.ts +1 -0
  30. package/dist/v2/usecases/console-service.js +4 -1
  31. package/dist/v2/usecases/console-types.d.ts +12 -0
  32. package/dist/v2/usecases/worktree-service.js +55 -7
  33. package/package.json +3 -2
  34. package/spec/authoring-spec.json +91 -3
  35. package/spec/workflow-tags.json +132 -0
  36. package/spec/workflow.schema.json +411 -97
  37. package/workflows/adaptive-ticket-creation.json +40 -22
  38. package/workflows/architecture-scalability-audit.json +65 -31
  39. package/workflows/bug-investigation.agentic.v2.json +36 -14
  40. package/workflows/coding-task-workflow-agentic.json +50 -38
  41. package/workflows/coding-task-workflow-agentic.lean.v2.json +124 -37
  42. package/workflows/coding-task-workflow-agentic.v2.json +90 -30
  43. package/workflows/cross-platform-code-conversion.v2.json +168 -48
  44. package/workflows/document-creation-workflow.json +47 -17
  45. package/workflows/documentation-update-workflow.json +8 -8
  46. package/workflows/intelligent-test-case-generation.json +2 -2
  47. package/workflows/learner-centered-course-workflow.json +267 -267
  48. package/workflows/mr-review-workflow.agentic.v2.json +81 -14
  49. package/workflows/personal-learning-materials-creation-branched.json +175 -175
  50. package/workflows/presentation-creation.json +159 -159
  51. package/workflows/production-readiness-audit.json +54 -15
  52. package/workflows/relocation-workflow-us.json +44 -35
  53. package/workflows/routines/tension-driven-design.json +1 -1
  54. package/workflows/scoped-documentation-workflow.json +25 -25
  55. package/workflows/test-artifact-loop-control.json +1 -2
  56. package/workflows/ui-ux-design-workflow.json +327 -0
  57. package/workflows/workflow-diagnose-environment.json +1 -1
  58. package/workflows/workflow-for-workflows.json +507 -484
  59. package/workflows/workflow-for-workflows.v2.json +90 -18
  60. package/workflows/wr.discovery.json +112 -30
  61. package/dist/console/assets/index-DW78t31j.css +0 -1
  62. package/dist/console/assets/index-EsSXrC_a.js +0 -28
@@ -3,9 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.shouldShowStaleness = shouldShowStaleness;
7
+ exports.buildTagSummary = buildTagSummary;
8
+ exports.computeWorkflowStaleness = computeWorkflowStaleness;
6
9
  exports.handleV2ListWorkflows = handleV2ListWorkflows;
7
10
  exports.handleV2InspectWorkflow = handleV2InspectWorkflow;
8
11
  const path_1 = __importDefault(require("path"));
12
+ const fs_1 = __importDefault(require("fs"));
9
13
  const neverthrow_1 = require("neverthrow");
10
14
  const types_js_1 = require("../types.js");
11
15
  const error_mapper_js_1 = require("../error-mapper.js");
@@ -14,6 +18,84 @@ const output_schemas_js_1 = require("../output-schemas.js");
14
18
  const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
15
19
  const hashing_js_1 = require("../../v2/durable-core/canonical/hashing.js");
16
20
  const TIMEOUT_MS = 30000;
21
+ function readCurrentSpecVersion() {
22
+ try {
23
+ const specPath = path_1.default.resolve(__dirname, '../../../spec/authoring-spec.json');
24
+ const raw = fs_1.default.readFileSync(specPath, 'utf-8');
25
+ const parsed = JSON.parse(raw);
26
+ if (typeof parsed === 'object' && parsed !== null && 'version' in parsed) {
27
+ const v = parsed['version'];
28
+ if (typeof v === 'number' && Number.isInteger(v) && v >= 1)
29
+ return v;
30
+ }
31
+ return null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ const CURRENT_SPEC_VERSION = readCurrentSpecVersion();
38
+ const DEV_STALENESS = process.env['WORKRAIL_DEV_STALENESS'] === '1';
39
+ function shouldShowStaleness(category, devMode = DEV_STALENESS) {
40
+ if (devMode)
41
+ return true;
42
+ return category === 'personal' || category === 'rooted_sharing' || category === 'external';
43
+ }
44
+ function readWorkflowTags() {
45
+ try {
46
+ const tagsPath = path_1.default.resolve(__dirname, '../../../spec/workflow-tags.json');
47
+ const raw = fs_1.default.readFileSync(tagsPath, 'utf-8');
48
+ return JSON.parse(raw);
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ const WORKFLOW_TAGS = readWorkflowTags();
55
+ function buildTagSummary(tagsFile, compiledWorkflowIds) {
56
+ const idSet = new Set(compiledWorkflowIds);
57
+ return tagsFile.tags.map((tag) => {
58
+ const count = Object.entries(tagsFile.workflows)
59
+ .filter(([wid, meta]) => !meta.hidden && idSet.has(wid) && meta.tags.includes(tag.id))
60
+ .length;
61
+ return {
62
+ id: tag.id,
63
+ displayName: tag.displayName,
64
+ count,
65
+ when: [...tag.when],
66
+ examples: [...tag.examples],
67
+ };
68
+ });
69
+ }
70
+ function filterByTags(tagsFile, compiledWorkflowIds, requestedTags) {
71
+ const tagSet = new Set(requestedTags);
72
+ const matching = new Set(Object.entries(tagsFile.workflows)
73
+ .filter(([, meta]) => !meta.hidden && meta.tags.some((t) => tagSet.has(t)))
74
+ .map(([wid]) => wid));
75
+ return compiledWorkflowIds.filter((id) => matching.has(id));
76
+ }
77
+ function computeWorkflowStaleness(stamp, currentVersion) {
78
+ if (currentVersion === null)
79
+ return undefined;
80
+ if (stamp === undefined) {
81
+ return {
82
+ level: 'possible',
83
+ reason: 'This workflow has not been validated against the authoring spec via workflow-for-workflows.',
84
+ };
85
+ }
86
+ if (stamp === currentVersion) {
87
+ return {
88
+ level: 'none',
89
+ reason: `Workflow validated against current authoring spec (v${currentVersion}).`,
90
+ specVersionAtLastReview: stamp,
91
+ };
92
+ }
93
+ return {
94
+ level: 'likely',
95
+ reason: `Authoring spec updated from v${stamp} to v${currentVersion} since this workflow was last reviewed.`,
96
+ specVersionAtLastReview: stamp,
97
+ };
98
+ }
17
99
  const with_timeout_js_1 = require("./shared/with-timeout.js");
18
100
  const request_workflow_reader_js_1 = require("./shared/request-workflow-reader.js");
19
101
  const remembered_roots_js_1 = require("./shared/remembered-roots.js");
@@ -42,10 +124,17 @@ async function handleV2ListWorkflows(input, ctx) {
42
124
  workspacePath: input.workspacePath,
43
125
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
44
126
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
127
+ managedSourceStore: guard.ctx.v2.managedSourceStore,
45
128
  })
46
- : { reader: ctx.workflowService, stalePaths: [] };
129
+ : { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
47
130
  const workflowReader = readerResult.reader;
48
131
  const stalePaths = readerResult.stalePaths;
132
+ const managedSourceRecords = readerResult.managedSourceRecords;
133
+ const staleManagedRecords = readerResult.staleManagedRecords;
134
+ const managedStoreError = readerResult.managedStoreError;
135
+ const warnings = managedStoreError
136
+ ? [`Managed workflow source store was temporarily unavailable (${managedStoreError}). Managed sources were not loaded.`]
137
+ : undefined;
49
138
  return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.listWorkflowSummaries(), TIMEOUT_MS, 'list_workflows'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
50
139
  .andThen((summaries) => neverthrow_1.ResultAsync.combine(summaries.map((s) => neverthrow_1.ResultAsync.fromPromise(buildV2WorkflowListItem({
51
140
  summary: s,
@@ -55,25 +144,62 @@ async function handleV2ListWorkflows(input, ctx) {
55
144
  pinnedStore,
56
145
  }), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)))))
57
146
  .andThen((compiled) => {
147
+ const sortedIds = compiled.map((w) => w.workflowId).sort((a, b) => a.localeCompare(b));
148
+ const sortedCompiled = [...compiled].sort((a, b) => a.workflowId.localeCompare(b.workflowId));
149
+ const tagFilteredCompiled = (() => {
150
+ if (input.includeSources)
151
+ return sortedCompiled;
152
+ if (!WORKFLOW_TAGS)
153
+ return sortedCompiled;
154
+ if (input.tags && input.tags.length > 0) {
155
+ const filteredIds = new Set(filterByTags(WORKFLOW_TAGS, sortedIds, input.tags));
156
+ return sortedCompiled.filter((w) => filteredIds.has(w.workflowId));
157
+ }
158
+ return [];
159
+ })();
160
+ const tagSummaryEntry = (() => {
161
+ if (input.includeSources)
162
+ return undefined;
163
+ if (!WORKFLOW_TAGS)
164
+ return undefined;
165
+ if (input.tags && input.tags.length > 0)
166
+ return undefined;
167
+ return buildTagSummary(WORKFLOW_TAGS, sortedIds);
168
+ })();
169
+ const nextStepHint = tagSummaryEntry
170
+ ? 'Pick a tag from tagSummary that fits the user\'s goal, then call list_workflows with tags=["<tagId>"]. ' +
171
+ 'If a workflow ID in examples[] already matches, call start_workflow directly — no second list call needed. ' +
172
+ 'If multiple tags could apply, pick the most specific one.'
173
+ : undefined;
58
174
  if (!input.includeSources) {
175
+ const includeStaleRoots = !tagSummaryEntry && stalePaths.length > 0;
59
176
  const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
60
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
61
- ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
177
+ workflows: tagFilteredCompiled,
178
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
179
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
180
+ ...(includeStaleRoots ? { staleRoots: [...stalePaths] } : {}),
181
+ ...(warnings ? { warnings } : {}),
62
182
  });
63
183
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
64
184
  }
65
185
  if (!(0, workflow_source_visibility_js_1.isCompositeWorkflowReader)(workflowReader)) {
66
186
  const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
67
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
187
+ workflows: tagFilteredCompiled,
188
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
189
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
68
190
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
191
+ ...(warnings ? { warnings } : {}),
69
192
  sources: [],
70
193
  });
71
194
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
72
195
  }
73
- return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
196
+ return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
74
197
  const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
75
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
198
+ workflows: tagFilteredCompiled,
199
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
200
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
76
201
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
202
+ ...(warnings ? { warnings } : {}),
77
203
  sources,
78
204
  });
79
205
  return (0, types_js_1.success)(payload);
@@ -102,10 +228,14 @@ async function handleV2InspectWorkflow(input, ctx) {
102
228
  workspacePath: input.workspacePath,
103
229
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
104
230
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
231
+ managedSourceStore: guard.ctx.v2.managedSourceStore,
105
232
  })
106
- : { reader: ctx.workflowService, stalePaths: [] };
233
+ : { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
107
234
  const workflowReader = readerResult.reader;
108
235
  const stalePaths = readerResult.stalePaths;
236
+ const inspectWarnings = readerResult.managedStoreError
237
+ ? [`Managed workflow source store was temporarily unavailable (${readerResult.managedStoreError}). Managed sources were not loaded.`]
238
+ : undefined;
109
239
  return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.getWorkflowById(input.workflowId), TIMEOUT_MS, 'inspect_workflow'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
110
240
  .andThen((workflow) => {
111
241
  if (!workflow) {
@@ -141,7 +271,14 @@ async function handleV2InspectWorkflow(input, ctx) {
141
271
  compiled: body,
142
272
  ...(visibility ? { visibility } : {}),
143
273
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
274
+ ...(inspectWarnings ? { warnings: inspectWarnings } : {}),
144
275
  ...(references != null && references.length > 0 ? { references } : {}),
276
+ ...(() => {
277
+ const staleness = shouldShowStaleness(visibility?.category)
278
+ ? computeWorkflowStaleness(workflow.definition.validatedAgainstSpecVersion, CURRENT_SPEC_VERSION)
279
+ : undefined;
280
+ return staleness !== undefined ? { staleness } : {};
281
+ })(),
145
282
  });
146
283
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
147
284
  }));
@@ -199,6 +336,9 @@ async function buildV2WorkflowListItem(options) {
199
336
  };
200
337
  }
201
338
  }
339
+ const staleness = shouldShowStaleness(visibility?.category)
340
+ ? computeWorkflowStaleness(workflow.definition.validatedAgainstSpecVersion, CURRENT_SPEC_VERSION)
341
+ : undefined;
202
342
  return {
203
343
  workflowId: summary.id,
204
344
  name: summary.name,
@@ -207,9 +347,10 @@ async function buildV2WorkflowListItem(options) {
207
347
  workflowHash: hash,
208
348
  kind: 'workflow',
209
349
  visibility,
350
+ ...(staleness !== undefined ? { staleness } : {}),
210
351
  };
211
352
  }
212
- async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
353
+ async function buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords) {
213
354
  const instances = workflowReader.getStorageInstances();
214
355
  const seenIds = new Set();
215
356
  const sourceEntryDataReversed = [];
@@ -223,10 +364,22 @@ async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
223
364
  sourceEntryDataReversed.push({ source: instance.source, allIds, effectiveIds });
224
365
  }
225
366
  const sourceEntryData = sourceEntryDataReversed.reverse();
226
- return sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, sourceEntryData }));
367
+ const activeEntries = sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, managedSourceRecords, sourceEntryData }));
368
+ const staleEntries = staleManagedRecords.map((record) => ({
369
+ sourceKey: `custom:${record.path}`,
370
+ category: 'managed',
371
+ source: { kind: 'custom', displayName: path_1.default.basename(record.path) },
372
+ sourceMode: 'live_directory',
373
+ effectiveWorkflowCount: 0,
374
+ totalWorkflowCount: 0,
375
+ shadowedWorkflowCount: 0,
376
+ managed: { addedAtMs: record.addedAtMs },
377
+ stale: true,
378
+ }));
379
+ return [...activeEntries, ...staleEntries];
227
380
  }
228
381
  function deriveSourceCatalogEntry(options) {
229
- const { source, allIds, effectiveIds, rememberedRootRecords, sourceEntryData } = options;
382
+ const { source, allIds, effectiveIds, rememberedRootRecords, managedSourceRecords, sourceEntryData } = options;
230
383
  const total = allIds.length;
231
384
  const effective = effectiveIds.length;
232
385
  const shadowed = total - effective;
@@ -252,6 +405,20 @@ function deriveSourceCatalogEntry(options) {
252
405
  }
253
406
  case 'custom': {
254
407
  const rootedSharing = deriveRootedSharingForPath(source.directoryPath, rememberedRootRecords);
408
+ const managedRecord = managedSourceRecords.find((r) => path_1.default.resolve(r.path) === path_1.default.resolve(source.directoryPath));
409
+ if (managedRecord) {
410
+ return {
411
+ sourceKey,
412
+ category: 'managed',
413
+ source: { kind: source.kind, displayName },
414
+ sourceMode: 'live_directory',
415
+ effectiveWorkflowCount: effective,
416
+ totalWorkflowCount: total,
417
+ shadowedWorkflowCount: shadowed,
418
+ managed: { addedAtMs: managedRecord.addedAtMs },
419
+ ...(rootedSharing ? { rootedSharing } : {}),
420
+ };
421
+ }
255
422
  const category = rootedSharing ? 'rooted_sharing' : 'external';
256
423
  const sourceMode = rootedSharing ? 'rooted_sharing' : 'live_directory';
257
424
  return { sourceKey, category, source: { kind: source.kind, displayName }, sourceMode, effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed, ...(rootedSharing ? { rootedSharing } : {}) };