@exaudeus/workrail 3.11.2 → 3.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 (77) hide show
  1. package/dist/console/assets/index-DW78t31j.css +1 -0
  2. package/dist/console/assets/index-EsSXrC_a.js +28 -0
  3. package/dist/console/index.html +2 -2
  4. package/dist/di/container.js +8 -0
  5. package/dist/di/tokens.d.ts +1 -0
  6. package/dist/di/tokens.js +1 -0
  7. package/dist/infrastructure/session/HttpServer.js +2 -14
  8. package/dist/manifest.json +139 -91
  9. package/dist/mcp/boundary-coercion.d.ts +2 -0
  10. package/dist/mcp/boundary-coercion.js +73 -0
  11. package/dist/mcp/handler-factory.d.ts +1 -1
  12. package/dist/mcp/handler-factory.js +13 -6
  13. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +10 -2
  14. package/dist/mcp/handlers/shared/request-workflow-reader.js +27 -10
  15. package/dist/mcp/handlers/shared/workflow-source-visibility.d.ts +3 -1
  16. package/dist/mcp/handlers/shared/workflow-source-visibility.js +7 -3
  17. package/dist/mcp/handlers/v2-execution/replay.js +25 -1
  18. package/dist/mcp/handlers/v2-execution/start.js +23 -17
  19. package/dist/mcp/handlers/v2-manage-workflow-source.d.ts +7 -0
  20. package/dist/mcp/handlers/v2-manage-workflow-source.js +50 -0
  21. package/dist/mcp/handlers/v2-workflow.js +123 -8
  22. package/dist/mcp/output-schemas.d.ts +393 -0
  23. package/dist/mcp/output-schemas.js +49 -1
  24. package/dist/mcp/server.js +2 -0
  25. package/dist/mcp/tool-descriptions.js +20 -0
  26. package/dist/mcp/tools.js +6 -0
  27. package/dist/mcp/types/tool-description-types.d.ts +1 -1
  28. package/dist/mcp/types/tool-description-types.js +1 -0
  29. package/dist/mcp/types/workflow-tool-edition.d.ts +1 -1
  30. package/dist/mcp/types.d.ts +2 -0
  31. package/dist/mcp/v2/tool-registry.js +8 -0
  32. package/dist/mcp/v2/tools.d.ts +15 -0
  33. package/dist/mcp/v2/tools.js +8 -1
  34. package/dist/v2/durable-core/constants.d.ts +1 -0
  35. package/dist/v2/durable-core/constants.js +2 -1
  36. package/dist/v2/durable-core/domain/observation-builder.d.ts +4 -1
  37. package/dist/v2/durable-core/domain/observation-builder.js +9 -0
  38. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +76 -16
  39. package/dist/v2/durable-core/schemas/session/events.d.ts +26 -5
  40. package/dist/v2/durable-core/schemas/session/events.js +2 -1
  41. package/dist/v2/infra/in-memory/managed-source-store/index.d.ts +8 -0
  42. package/dist/v2/infra/in-memory/managed-source-store/index.js +33 -0
  43. package/dist/v2/infra/local/data-dir/index.d.ts +2 -0
  44. package/dist/v2/infra/local/data-dir/index.js +6 -0
  45. package/dist/v2/infra/local/managed-source-store/index.d.ts +15 -0
  46. package/dist/v2/infra/local/managed-source-store/index.js +164 -0
  47. package/dist/v2/infra/local/session-summary-provider/index.js +2 -0
  48. package/dist/v2/infra/local/workspace-anchor/index.js +1 -0
  49. package/dist/v2/ports/data-dir.port.d.ts +2 -0
  50. package/dist/v2/ports/managed-source-store.port.d.ts +25 -0
  51. package/dist/v2/ports/managed-source-store.port.js +2 -0
  52. package/dist/v2/ports/workspace-anchor.port.d.ts +3 -0
  53. package/dist/v2/projections/resume-ranking.d.ts +1 -0
  54. package/dist/v2/usecases/console-routes.js +26 -0
  55. package/dist/v2/usecases/console-service.js +25 -6
  56. package/dist/v2/usecases/console-types.d.ts +22 -1
  57. package/dist/v2/usecases/worktree-service.d.ts +10 -0
  58. package/dist/v2/usecases/worktree-service.js +136 -0
  59. package/package.json +1 -1
  60. package/workflows/adaptive-ticket-creation.json +276 -282
  61. package/workflows/architecture-scalability-audit.json +317 -0
  62. package/workflows/document-creation-workflow.json +70 -191
  63. package/workflows/documentation-update-workflow.json +59 -309
  64. package/workflows/intelligent-test-case-generation.json +37 -212
  65. package/workflows/personal-learning-materials-creation-branched.json +1 -21
  66. package/workflows/presentation-creation.json +143 -308
  67. package/workflows/relocation-workflow-us.json +161 -535
  68. package/workflows/routines/tension-driven-design.json +5 -5
  69. package/workflows/scoped-documentation-workflow.json +110 -181
  70. package/workflows/workflow-for-workflows.v2.json +21 -5
  71. package/dist/console/assets/index-C5C4nDs4.css +0 -1
  72. package/dist/console/assets/index-CSUqsoQl.js +0 -28
  73. package/workflows/CHANGELOG-bug-investigation.md +0 -298
  74. package/workflows/bug-investigation.agentic.json +0 -212
  75. package/workflows/bug-investigation.json +0 -112
  76. package/workflows/mr-review-workflow.agentic.json +0 -538
  77. package/workflows/mr-review-workflow.json +0 -277
@@ -4,6 +4,7 @@ exports.toMcpResult = toMcpResult;
4
4
  exports.createHandler = createHandler;
5
5
  exports.createValidatingHandler = createValidatingHandler;
6
6
  const types_js_1 = require("./types.js");
7
+ const boundary_coercion_js_1 = require("./boundary-coercion.js");
7
8
  const index_js_1 = require("./validation/index.js");
8
9
  const bounded_json_js_1 = require("./validation/bounded-json.js");
9
10
  const v2_execution_helpers_js_1 = require("./handlers/v2-execution-helpers.js");
@@ -56,12 +57,15 @@ function toMcpResult(result) {
56
57
  }
57
58
  function createHandler(schema, handler, shapeSchema, aliasMap) {
58
59
  return async (args, ctx) => {
59
- const parseResult = schema.safeParse(args);
60
+ const normalizedArgs = shapeSchema !== undefined
61
+ ? (0, boundary_coercion_js_1.coerceJsonStringObjectFields)(args, shapeSchema, aliasMap)
62
+ : args;
63
+ const parseResult = schema.safeParse(normalizedArgs);
60
64
  if (!parseResult.success) {
61
65
  const introspectionSchema = shapeSchema ?? schema;
62
- const suggestionResult = (0, index_js_1.generateSuggestions)(args, introspectionSchema, index_js_1.DEFAULT_SUGGESTION_CONFIG, aliasMap);
66
+ const suggestionResult = (0, index_js_1.generateSuggestions)(normalizedArgs, introspectionSchema, index_js_1.DEFAULT_SUGGESTION_CONFIG, aliasMap);
63
67
  const suggestionDetails = (0, index_js_1.formatSuggestionDetails)(suggestionResult);
64
- const patchedTemplate = (0, index_js_1.patchTemplateForFailedOptionals)(suggestionDetails.correctTemplate ?? null, args, parseResult.error.errors, introspectionSchema, index_js_1.DEFAULT_SUGGESTION_CONFIG.maxTemplateDepth);
68
+ const patchedTemplate = (0, index_js_1.patchTemplateForFailedOptionals)(suggestionDetails.correctTemplate ?? null, normalizedArgs, parseResult.error.errors, introspectionSchema, index_js_1.DEFAULT_SUGGESTION_CONFIG.maxTemplateDepth);
65
69
  const patchedDetails = patchedTemplate !== suggestionDetails.correctTemplate
66
70
  ? { ...suggestionDetails, correctTemplate: patchedTemplate }
67
71
  : suggestionDetails;
@@ -82,9 +86,12 @@ function createHandler(schema, handler, shapeSchema, aliasMap) {
82
86
  }
83
87
  };
84
88
  }
85
- function createValidatingHandler(schema, preValidate, handler) {
89
+ function createValidatingHandler(schema, preValidate, handler, shapeSchema, aliasMap) {
86
90
  return async (args, ctx) => {
87
- const pre = preValidate(args);
91
+ const normalizedArgs = shapeSchema !== undefined
92
+ ? (0, boundary_coercion_js_1.coerceJsonStringObjectFields)(args, shapeSchema, aliasMap)
93
+ : args;
94
+ const pre = preValidate(normalizedArgs);
88
95
  if (!pre.ok) {
89
96
  const error = pre.error;
90
97
  const details = error.details && typeof error.details === 'object' ? error.details : {};
@@ -103,6 +110,6 @@ function createValidatingHandler(schema, preValidate, handler) {
103
110
  }
104
111
  return toMcpResult(error);
105
112
  }
106
- return createHandler(schema, handler)(args, ctx);
113
+ return createHandler(schema, handler, shapeSchema, aliasMap)(normalizedArgs, ctx);
107
114
  };
108
115
  }
@@ -18,5 +18,13 @@ export declare function resolveRequestWorkspaceDirectory(options: {
18
18
  readonly serverCwd?: string;
19
19
  }): string;
20
20
  export declare function toProjectWorkflowDirectory(workspaceDirectory: string): string;
21
- export declare function discoverRootedWorkflowDirectories(roots: readonly string[]): Promise<readonly string[]>;
22
- export declare function createWorkflowReaderForRequest(options: RequestWorkflowReaderOptions): Promise<IWorkflowReader>;
21
+ export interface WorkflowRootDiscoveryResult {
22
+ readonly discovered: readonly string[];
23
+ readonly stale: readonly string[];
24
+ }
25
+ export declare function discoverRootedWorkflowDirectories(roots: readonly string[]): Promise<WorkflowRootDiscoveryResult>;
26
+ export interface WorkflowReaderForRequestResult {
27
+ readonly reader: IWorkflowReader;
28
+ readonly stalePaths: readonly string[];
29
+ }
30
+ export declare function createWorkflowReaderForRequest(options: RequestWorkflowReaderOptions): Promise<WorkflowReaderForRequestResult>;
@@ -37,10 +37,15 @@ function toProjectWorkflowDirectory(workspaceDirectory) {
37
37
  async function discoverRootedWorkflowDirectories(roots) {
38
38
  const discoveredByPath = new Set();
39
39
  const discoveredPaths = [];
40
+ const stalePaths = [];
40
41
  for (const root of roots) {
41
42
  const rootPath = path_1.default.resolve(root);
42
- const nextPaths = await discoverWorkflowDirectoriesUnderRoot(rootPath);
43
- for (const nextPath of nextPaths) {
43
+ const result = await discoverWorkflowDirectoriesUnderRoot(rootPath);
44
+ if (result.stale) {
45
+ stalePaths.push(rootPath);
46
+ continue;
47
+ }
48
+ for (const nextPath of result.discovered) {
44
49
  const normalizedPath = path_1.default.resolve(nextPath);
45
50
  if (discoveredByPath.has(normalizedPath))
46
51
  continue;
@@ -48,19 +53,20 @@ async function discoverRootedWorkflowDirectories(roots) {
48
53
  discoveredPaths.push(normalizedPath);
49
54
  }
50
55
  }
51
- return discoveredPaths;
56
+ return { discovered: discoveredPaths, stale: stalePaths };
52
57
  }
53
58
  async function createWorkflowReaderForRequest(options) {
54
59
  const workspaceDirectory = resolveRequestWorkspaceDirectory(options);
55
60
  const projectWorkflowDirectory = toProjectWorkflowDirectory(workspaceDirectory);
56
61
  const rememberedRoots = await listRememberedRoots(options.rememberedRootsStore);
57
- const rootedWorkflowDirectories = await discoverRootedWorkflowDirectories(rememberedRoots);
62
+ const { discovered: rootedWorkflowDirectories, stale: stalePaths } = await discoverRootedWorkflowDirectories(rememberedRoots);
58
63
  const customPaths = rootedWorkflowDirectories.filter((directory) => directory !== projectWorkflowDirectory);
59
- const storage = new enhanced_multi_source_workflow_storage_js_1.EnhancedMultiSourceWorkflowStorage({
64
+ const storage = (0, enhanced_multi_source_workflow_storage_js_1.createEnhancedMultiSourceWorkflowStorage)({
60
65
  customPaths,
61
66
  projectPath: projectWorkflowDirectory,
62
- }, options.featureFlags);
63
- return new schema_validating_workflow_storage_js_1.SchemaValidatingCompositeWorkflowStorage(storage);
67
+ }, options.featureFlags ?? undefined);
68
+ const reader = new schema_validating_workflow_storage_js_1.SchemaValidatingCompositeWorkflowStorage(storage);
69
+ return { reader, stalePaths };
64
70
  }
65
71
  async function listRememberedRoots(rememberedRootsStore) {
66
72
  if (!rememberedRootsStore)
@@ -74,8 +80,16 @@ async function listRememberedRoots(rememberedRootsStore) {
74
80
  }
75
81
  async function discoverWorkflowDirectoriesUnderRoot(rootPath) {
76
82
  const discoveredPaths = [];
77
- await walkForRootedWorkflowDirectories(rootPath, discoveredPaths);
78
- return discoveredPaths;
83
+ try {
84
+ await walkForRootedWorkflowDirectories(rootPath, discoveredPaths);
85
+ }
86
+ catch (err) {
87
+ if (err.code === 'ENOENT') {
88
+ return { discovered: [], stale: true };
89
+ }
90
+ throw err;
91
+ }
92
+ return { discovered: discoveredPaths, stale: false };
79
93
  }
80
94
  async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPaths) {
81
95
  const entries = await promises_1.default.readdir(currentDirectory, { withFileTypes: true });
@@ -93,7 +107,10 @@ async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPath
93
107
  }
94
108
  continue;
95
109
  }
96
- await walkForRootedWorkflowDirectories(entryPath, discoveredPaths);
110
+ await walkForRootedWorkflowDirectories(entryPath, discoveredPaths).catch((err) => {
111
+ if (err.code !== 'ENOENT')
112
+ throw err;
113
+ });
97
114
  }
98
115
  }
99
116
  function shouldSkipDirectory(name) {
@@ -1,5 +1,5 @@
1
1
  import type { Workflow, WorkflowSourceInfo } from '../../../types/workflow.js';
2
- import type { IWorkflowReader } from '../../../types/storage.js';
2
+ import type { ICompositeWorkflowStorage, IWorkflowReader } from '../../../types/storage.js';
3
3
  import type { RememberedRootRecordV2 } from '../../../v2/ports/remembered-roots-store.port.js';
4
4
  export interface PublicWorkflowSource {
5
5
  readonly kind: WorkflowSourceInfo['kind'];
@@ -31,3 +31,5 @@ export declare function detectWorkflowMigrationGuidance(options: {
31
31
  readonly workflowReader: IWorkflowReader;
32
32
  readonly rememberedRoots: readonly RememberedRootRecordV2[];
33
33
  }): Promise<WorkflowMigrationGuidance | undefined>;
34
+ export declare function isCompositeWorkflowReader(reader: unknown): reader is ICompositeWorkflowStorage;
35
+ export declare function deriveGroupLabel(rootPath: string, sourcePath: string): string;
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.toWorkflowVisibility = toWorkflowVisibility;
7
7
  exports.detectWorkflowMigrationGuidance = detectWorkflowMigrationGuidance;
8
+ exports.isCompositeWorkflowReader = isCompositeWorkflowReader;
9
+ exports.deriveGroupLabel = deriveGroupLabel;
8
10
  const path_1 = __importDefault(require("path"));
9
11
  function toWorkflowVisibility(workflow, rememberedRoots, options = {}) {
10
12
  const source = {
@@ -56,9 +58,11 @@ async function detectWorkflowMigrationGuidance(options) {
56
58
  }
57
59
  return undefined;
58
60
  }
59
- function isCompositeWorkflowReader(workflowReader) {
60
- return (workflowReader.kind === 'composite' &&
61
- typeof workflowReader.getStorageInstances === 'function');
61
+ function isCompositeWorkflowReader(reader) {
62
+ return (typeof reader === 'object' &&
63
+ reader !== null &&
64
+ reader.kind === 'composite' &&
65
+ typeof reader.getStorageInstances === 'function');
62
66
  }
63
67
  function deriveRootedSharingContext(workflow, rememberedRoots) {
64
68
  if (workflow.source.kind !== 'custom')
@@ -14,8 +14,9 @@ const v2_token_ops_js_1 = require("../v2-token-ops.js");
14
14
  const v2_state_conversion_js_1 = require("../v2-state-conversion.js");
15
15
  const constants_js_1 = require("../../../v2/durable-core/constants.js");
16
16
  const index_js_2 = require("./index.js");
17
+ const assessments_js_1 = require("../../../v2/projections/assessments.js");
17
18
  function buildAdvancedReplayResponse(args) {
18
- const { sessionId, runId, toNodeId, attemptId, toSnapshot, workflow, truth, workflowHash, ports, sha256, aliasStore, entropy } = args;
19
+ const { sessionId, runId, fromNodeId, toNodeId, attemptId, toSnapshot, workflow, truth, workflowHash, ports, sha256, aliasStore, entropy } = args;
19
20
  const toNodeIdBranded = (0, index_js_1.asNodeId)(String(toNodeId));
20
21
  const pending = (0, snapshot_state_js_1.derivePendingStep)(toSnapshot.enginePayload.engineState);
21
22
  const isComplete = (0, snapshot_state_js_1.deriveIsComplete)(toSnapshot.enginePayload.engineState);
@@ -116,6 +117,7 @@ function buildAdvancedReplayResponse(args) {
116
117
  }
117
118
  const preferences = (0, v2_execution_helpers_js_1.derivePreferencesOrDefault)({ truth, runId, nodeId: toNodeIdBranded });
118
119
  const nextIntent = (0, v2_state_conversion_js_1.deriveNextIntent)({ rehydrateOnly: false, isComplete, pending: okMeta });
120
+ const stepContext = buildStepContext(truth.events, fromNodeId);
119
121
  return nextTokensMint.andThen((nextTokens) => (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
120
122
  kind: 'ok',
121
123
  continueToken: pending ? nextTokens.continueToken : undefined,
@@ -125,8 +127,30 @@ function buildAdvancedReplayResponse(args) {
125
127
  preferences,
126
128
  nextIntent,
127
129
  nextCall: (0, index_js_2.buildNextCall)({ continueToken: pending ? nextTokens.continueToken : undefined, isComplete, pending: okMeta }),
130
+ stepContext,
128
131
  })));
129
132
  }
133
+ function buildStepContext(events, completedNodeId) {
134
+ const projection = (0, assessments_js_1.projectAssessmentsV2)(events);
135
+ if (projection.isErr()) {
136
+ console.warn(`[workrail:replay] stepContext projection failed for node '${String(completedNodeId)}' — stepContext will be absent: ${projection.error.message}`);
137
+ return undefined;
138
+ }
139
+ const recorded = (0, assessments_js_1.getLatestAssessmentForNode)(projection.value, String(completedNodeId));
140
+ if (!recorded)
141
+ return undefined;
142
+ return {
143
+ assessments: {
144
+ assessmentId: recorded.assessmentId,
145
+ dimensions: recorded.dimensions.map((d) => ({
146
+ dimensionId: d.dimensionId,
147
+ level: d.level,
148
+ ...(d.rationale !== undefined ? { rationale: d.rationale } : {}),
149
+ })),
150
+ normalizationNotes: recorded.normalizationNotes,
151
+ },
152
+ };
153
+ }
130
154
  function replayFromRecordedAdvance(args) {
131
155
  const { recordedEvent, truth, sessionId, runId, nodeId, workflowHash, attemptId, pinnedWorkflow, snapshotStore, sha256, tokenCodecPorts, aliasStore, entropy, } = args;
132
156
  if (recordedEvent.data.outcome.kind === 'blocked') {
@@ -216,23 +216,28 @@ function executeStartWorkflow(input, ctx) {
216
216
  workspacePath: input.workspacePath,
217
217
  resolvedRootUris: ctx.v2.resolvedRootUris,
218
218
  });
219
- const workflowReader = shouldUseRequestReader
220
- ? {
221
- getWorkflowById: async (workflowId) => {
222
- const requestReader = await (0, request_workflow_reader_js_1.createWorkflowReaderForRequest)({
223
- featureFlags: ctx.featureFlags,
224
- workspacePath: input.workspacePath,
225
- resolvedRootUris: ctx.v2.resolvedRootUris,
226
- rememberedRootsStore: ctx.v2.rememberedRootsStore,
227
- });
228
- const requestResult = await requestReader.getWorkflowById(workflowId);
229
- if (requestResult != null)
230
- return requestResult;
231
- return ctx.workflowService.getWorkflowById(workflowId);
219
+ const readerRA = shouldUseRequestReader
220
+ ? neverthrow_1.ResultAsync.fromPromise((0, request_workflow_reader_js_1.createWorkflowReaderForRequest)({
221
+ featureFlags: ctx.featureFlags,
222
+ workspacePath: input.workspacePath,
223
+ resolvedRootUris: ctx.v2.resolvedRootUris,
224
+ rememberedRootsStore: ctx.v2.rememberedRootsStore,
225
+ }), (err) => ({
226
+ kind: 'precondition_failed',
227
+ message: `Failed to initialize workflow reader: ${String(err)}`,
228
+ })).map(({ reader, stalePaths }) => ({
229
+ workflowReader: {
230
+ getWorkflowById: async (workflowId) => {
231
+ const requestResult = await reader.getWorkflowById(workflowId);
232
+ if (requestResult != null)
233
+ return requestResult;
234
+ return ctx.workflowService.getWorkflowById(workflowId);
235
+ },
232
236
  },
233
- }
234
- : ctx.workflowService;
235
- return loadAndPinWorkflow({
237
+ stalePaths,
238
+ }))
239
+ : (0, neverthrow_1.okAsync)({ workflowReader: ctx.workflowService, stalePaths: [] });
240
+ return readerRA.andThen(({ workflowReader, stalePaths }) => loadAndPinWorkflow({
236
241
  workflowId: input.workflowId,
237
242
  workflowReader,
238
243
  crypto,
@@ -345,10 +350,11 @@ function executeStartWorkflow(input, ctx) {
345
350
  preferences,
346
351
  nextIntent,
347
352
  nextCall: (0, index_js_2.buildNextCall)({ continueToken: tokens.continueToken, isComplete: false, pending }),
353
+ ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
348
354
  });
349
355
  return (0, neverthrow_1.okAsync)({ response: parsed, contentEnvelope });
350
356
  });
351
- });
357
+ }));
352
358
  }
353
359
  function enrichPinnedSnapshotWithResolvedReferences(snapshot, references, workspacePath) {
354
360
  if (references.length === 0) {
@@ -0,0 +1,7 @@
1
+ import type { ToolContext, ToolResult } from '../types.js';
2
+ import type { V2ManageWorkflowSourceInput } from '../v2/tools.js';
3
+ export interface ManageWorkflowSourceOutput {
4
+ readonly action: 'attach' | 'detach';
5
+ readonly path: string;
6
+ }
7
+ export declare function handleV2ManageWorkflowSource(input: V2ManageWorkflowSourceInput, ctx: ToolContext): Promise<ToolResult<ManageWorkflowSourceOutput>>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleV2ManageWorkflowSource = handleV2ManageWorkflowSource;
7
+ const path_1 = __importDefault(require("path"));
8
+ const assert_never_js_1 = require("../../runtime/assert-never.js");
9
+ const types_js_1 = require("../types.js");
10
+ function mapManagedSourceErrorToToolError(error) {
11
+ switch (error.code) {
12
+ case 'MANAGED_SOURCE_BUSY':
13
+ return (0, types_js_1.errRetryAfterMs)('INTERNAL_ERROR', 'WorkRail is temporarily busy updating managed workflow sources.', error.retry.afterMs, {
14
+ suggestion: 'Wait a moment and retry. Another WorkRail process may be updating managed workflow sources.',
15
+ });
16
+ case 'MANAGED_SOURCE_IO_ERROR':
17
+ return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail could not update the managed workflow sources store.', {
18
+ suggestion: 'Fix WorkRail local storage access and retry. Check that the ~/.workrail data directory exists and is writable.',
19
+ details: { errorMessage: error.message },
20
+ });
21
+ case 'MANAGED_SOURCE_CORRUPTION':
22
+ return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail managed workflow sources store is corrupted.', {
23
+ suggestion: 'The managed sources file may need to be deleted and re-created. Check the ~/.workrail/data/managed-sources directory.',
24
+ details: { errorMessage: error.message },
25
+ });
26
+ default: {
27
+ const _exhaustive = error;
28
+ return (0, assert_never_js_1.assertNever)(_exhaustive);
29
+ }
30
+ }
31
+ }
32
+ async function handleV2ManageWorkflowSource(input, ctx) {
33
+ const guard = (0, types_js_1.requireV2Context)(ctx);
34
+ if (!guard.ok)
35
+ return guard.error;
36
+ const { managedSourceStore } = guard.ctx.v2;
37
+ if (!managedSourceStore) {
38
+ return (0, types_js_1.errNotRetryable)('PRECONDITION_FAILED', 'Managed workflow source store is not available.', {
39
+ suggestion: 'Ensure WorkRail v2 is fully initialized. The managedSourceStore dependency may not be wired in this environment.',
40
+ });
41
+ }
42
+ const operation = input.action === 'attach'
43
+ ? managedSourceStore.attach(input.path)
44
+ : managedSourceStore.detach(input.path);
45
+ const result = await operation;
46
+ if (result.isErr()) {
47
+ return mapManagedSourceErrorToToolError(result.error);
48
+ }
49
+ return (0, types_js_1.success)({ action: input.action, path: path_1.default.resolve(input.path) });
50
+ }
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.handleV2ListWorkflows = handleV2ListWorkflows;
4
7
  exports.handleV2InspectWorkflow = handleV2InspectWorkflow;
8
+ const path_1 = __importDefault(require("path"));
5
9
  const neverthrow_1 = require("neverthrow");
6
10
  const types_js_1 = require("../types.js");
7
11
  const error_mapper_js_1 = require("../error-mapper.js");
@@ -29,7 +33,7 @@ async function handleV2ListWorkflows(input, ctx) {
29
33
  return rememberedRootRecordsResult;
30
34
  const rememberedRootRecords = rememberedRootRecordsResult;
31
35
  const { crypto, pinnedStore } = guard.ctx.v2;
32
- const workflowReader = (0, request_workflow_reader_js_1.hasRequestWorkspaceSignal)({
36
+ const readerResult = (0, request_workflow_reader_js_1.hasRequestWorkspaceSignal)({
33
37
  workspacePath: input.workspacePath,
34
38
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
35
39
  })
@@ -39,7 +43,9 @@ async function handleV2ListWorkflows(input, ctx) {
39
43
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
40
44
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
41
45
  })
42
- : ctx.workflowService;
46
+ : { reader: ctx.workflowService, stalePaths: [] };
47
+ const workflowReader = readerResult.reader;
48
+ const stalePaths = readerResult.stalePaths;
43
49
  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))
44
50
  .andThen((summaries) => neverthrow_1.ResultAsync.combine(summaries.map((s) => neverthrow_1.ResultAsync.fromPromise(buildV2WorkflowListItem({
45
51
  summary: s,
@@ -48,11 +54,30 @@ async function handleV2ListWorkflows(input, ctx) {
48
54
  crypto,
49
55
  pinnedStore,
50
56
  }), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)))))
51
- .map((compiled) => {
52
- const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
53
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
57
+ .andThen((compiled) => {
58
+ if (!input.includeSources) {
59
+ 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] } : {}),
62
+ });
63
+ return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
64
+ }
65
+ if (!(0, workflow_source_visibility_js_1.isCompositeWorkflowReader)(workflowReader)) {
66
+ const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
67
+ workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
68
+ ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
69
+ sources: [],
70
+ });
71
+ return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
72
+ }
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) => {
74
+ const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
75
+ workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
76
+ ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
77
+ sources,
78
+ });
79
+ return (0, types_js_1.success)(payload);
54
80
  });
55
- return (0, types_js_1.success)(payload);
56
81
  })
57
82
  .match((result) => Promise.resolve(result), (err) => Promise.resolve(err));
58
83
  }
@@ -68,7 +93,7 @@ async function handleV2InspectWorkflow(input, ctx) {
68
93
  return rememberedRootRecordsResult;
69
94
  const rememberedRootRecords = rememberedRootRecordsResult;
70
95
  const { crypto, pinnedStore } = guard.ctx.v2;
71
- const workflowReader = (0, request_workflow_reader_js_1.hasRequestWorkspaceSignal)({
96
+ const readerResult = (0, request_workflow_reader_js_1.hasRequestWorkspaceSignal)({
72
97
  workspacePath: input.workspacePath,
73
98
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
74
99
  })
@@ -78,7 +103,9 @@ async function handleV2InspectWorkflow(input, ctx) {
78
103
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
79
104
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
80
105
  })
81
- : ctx.workflowService;
106
+ : { reader: ctx.workflowService, stalePaths: [] };
107
+ const workflowReader = readerResult.reader;
108
+ const stalePaths = readerResult.stalePaths;
82
109
  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))
83
110
  .andThen((workflow) => {
84
111
  if (!workflow) {
@@ -113,6 +140,7 @@ async function handleV2InspectWorkflow(input, ctx) {
113
140
  mode: input.mode,
114
141
  compiled: body,
115
142
  ...(visibility ? { visibility } : {}),
143
+ ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
116
144
  ...(references != null && references.length > 0 ? { references } : {}),
117
145
  });
118
146
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
@@ -181,3 +209,90 @@ async function buildV2WorkflowListItem(options) {
181
209
  visibility,
182
210
  };
183
211
  }
212
+ async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
213
+ const instances = workflowReader.getStorageInstances();
214
+ const seenIds = new Set();
215
+ const sourceEntryDataReversed = [];
216
+ for (let i = instances.length - 1; i >= 0; i--) {
217
+ const instance = instances[i];
218
+ const summaries = await instance.listWorkflowSummaries();
219
+ const allIds = summaries.map((s) => s.id);
220
+ const effectiveIds = allIds.filter((id) => !seenIds.has(id));
221
+ for (const id of allIds)
222
+ seenIds.add(id);
223
+ sourceEntryDataReversed.push({ source: instance.source, allIds, effectiveIds });
224
+ }
225
+ const sourceEntryData = sourceEntryDataReversed.reverse();
226
+ return sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, sourceEntryData }));
227
+ }
228
+ function deriveSourceCatalogEntry(options) {
229
+ const { source, allIds, effectiveIds, rememberedRootRecords, sourceEntryData } = options;
230
+ const total = allIds.length;
231
+ const effective = effectiveIds.length;
232
+ const shadowed = total - effective;
233
+ const sourceKey = deriveSourceKey(source);
234
+ const displayName = deriveDisplayName(source);
235
+ switch (source.kind) {
236
+ case 'bundled':
237
+ return { sourceKey, category: 'built_in', source: { kind: source.kind, displayName }, sourceMode: 'built_in', effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed };
238
+ case 'user':
239
+ return { sourceKey, category: 'personal', source: { kind: source.kind, displayName }, sourceMode: 'personal', effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed };
240
+ case 'project': {
241
+ const thisIds = new Set(allIds);
242
+ const hasMigrationOverlap = sourceEntryData.some((e) => {
243
+ if (e.source === source || e.source.kind !== 'custom')
244
+ return false;
245
+ const rootedSharing = deriveRootedSharingForPath(e.source.directoryPath, rememberedRootRecords);
246
+ return rootedSharing != null && e.allIds.some((id) => thisIds.has(id));
247
+ });
248
+ const migration = hasMigrationOverlap
249
+ ? { preferredSource: 'rooted_sharing', currentSource: 'legacy_project', reason: 'legacy_project_precedence', summary: 'Project-scoped ./workflows currently overrides rooted .workrail/workflows during migration. Prefer rooted sharing for new team-shared workflows.' }
250
+ : undefined;
251
+ return { sourceKey, category: 'legacy_project', source: { kind: source.kind, displayName }, sourceMode: 'legacy_project', effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed, ...(migration ? { migration } : {}) };
252
+ }
253
+ case 'custom': {
254
+ const rootedSharing = deriveRootedSharingForPath(source.directoryPath, rememberedRootRecords);
255
+ const category = rootedSharing ? 'rooted_sharing' : 'external';
256
+ const sourceMode = rootedSharing ? 'rooted_sharing' : 'live_directory';
257
+ return { sourceKey, category, source: { kind: source.kind, displayName }, sourceMode, effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed, ...(rootedSharing ? { rootedSharing } : {}) };
258
+ }
259
+ case 'git':
260
+ case 'remote':
261
+ case 'plugin':
262
+ return { sourceKey, category: 'external', source: { kind: source.kind, displayName }, sourceMode: 'live_directory', effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed };
263
+ }
264
+ }
265
+ function deriveSourceKey(source) {
266
+ switch (source.kind) {
267
+ case 'bundled': return 'built_in';
268
+ case 'user': return `user:${source.directoryPath}`;
269
+ case 'project': return `project:${source.directoryPath}`;
270
+ case 'custom': return `custom:${source.directoryPath}`;
271
+ case 'git': return `git:${source.repositoryUrl}`;
272
+ case 'remote': return `remote:${source.registryUrl}`;
273
+ case 'plugin': return `plugin:${source.pluginName}`;
274
+ }
275
+ }
276
+ function deriveDisplayName(source) {
277
+ switch (source.kind) {
278
+ case 'bundled': return 'Built-in';
279
+ case 'user': return 'User Library';
280
+ case 'project': return 'Project';
281
+ case 'custom': return source.label ?? path_1.default.basename(source.directoryPath);
282
+ case 'git': return source.repositoryUrl;
283
+ case 'remote': return source.registryUrl;
284
+ case 'plugin': return source.pluginName;
285
+ }
286
+ }
287
+ function deriveRootedSharingForPath(sourcePath, rememberedRoots) {
288
+ const resolved = path_1.default.resolve(sourcePath);
289
+ for (const record of rememberedRoots) {
290
+ const rootPath = path_1.default.resolve(record.path);
291
+ const relative = path_1.default.relative(rootPath, resolved);
292
+ const isUnderRoot = relative.length === 0 || (!relative.startsWith('..') && !path_1.default.isAbsolute(relative));
293
+ if (!isUnderRoot)
294
+ continue;
295
+ return { kind: 'remembered_root', rootPath, groupLabel: (0, workflow_source_visibility_js_1.deriveGroupLabel)(rootPath, resolved) };
296
+ }
297
+ return undefined;
298
+ }