@exaudeus/workrail 3.16.0 → 3.17.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 (55) hide show
  1. package/dist/application/use-cases/raw-workflow-file-scanner.js +10 -13
  2. package/dist/cli/commands/index.d.ts +1 -1
  3. package/dist/cli/commands/index.js +2 -1
  4. package/dist/cli/commands/init.d.ts +10 -0
  5. package/dist/cli/commands/init.js +72 -0
  6. package/dist/cli.js +13 -1
  7. package/dist/config/config-file.d.ts +8 -0
  8. package/dist/config/config-file.js +141 -0
  9. package/dist/config/feature-flags.js +8 -0
  10. package/dist/console/assets/index-BwJelCXK.js +28 -0
  11. package/dist/console/index.html +1 -1
  12. package/dist/di/container.d.ts +1 -0
  13. package/dist/di/container.js +24 -7
  14. package/dist/infrastructure/session/HttpServer.d.ts +0 -1
  15. package/dist/infrastructure/session/HttpServer.js +4 -46
  16. package/dist/manifest.json +101 -109
  17. package/dist/mcp/assert-output.js +2 -1
  18. package/dist/mcp/dev-mode.d.ts +1 -0
  19. package/dist/mcp/dev-mode.js +12 -0
  20. package/dist/mcp/handler-factory.d.ts +1 -1
  21. package/dist/mcp/handler-factory.js +8 -7
  22. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +1 -0
  23. package/dist/mcp/handlers/shared/request-workflow-reader.js +90 -20
  24. package/dist/mcp/handlers/v2-execution/continue-advance.d.ts +1 -0
  25. package/dist/mcp/handlers/v2-execution/continue-advance.js +3 -1
  26. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -0
  27. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +2 -1
  28. package/dist/mcp/handlers/v2-execution/index.js +2 -0
  29. package/dist/mcp/handlers/v2-execution/replay.d.ts +2 -0
  30. package/dist/mcp/handlers/v2-execution/replay.js +3 -0
  31. package/dist/mcp/handlers/v2-execution/start.js +48 -20
  32. package/dist/mcp/handlers/v2-workflow.js +4 -2
  33. package/dist/mcp/output-schemas.d.ts +5 -0
  34. package/dist/mcp/output-schemas.js +2 -0
  35. package/dist/mcp/server.js +3 -2
  36. package/dist/mcp/v2-response-formatter.d.ts +1 -1
  37. package/dist/mcp/v2-response-formatter.js +2 -3
  38. package/dist/types/workflow-definition.d.ts +3 -1
  39. package/dist/types/workflow-definition.js +2 -0
  40. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
  41. package/dist/v2/durable-core/domain/prompt-renderer.js +1 -2
  42. package/dist/v2/durable-core/schemas/compiled-workflow/index.js +4 -3
  43. package/dist/v2/infra/local/pinned-workflow-store/index.d.ts +2 -0
  44. package/dist/v2/infra/local/pinned-workflow-store/index.js +49 -0
  45. package/dist/v2/infra/local/remembered-roots-store/index.d.ts +3 -1
  46. package/dist/v2/infra/local/remembered-roots-store/index.js +6 -3
  47. package/dist/v2/infra/local/workspace-anchor/index.js +4 -2
  48. package/dist/v2/ports/pinned-workflow-store.port.d.ts +2 -0
  49. package/dist/v2/usecases/console-routes.js +3 -2
  50. package/package.json +1 -1
  51. package/dist/console/assets/index-BE5PAgPO.js +0 -28
  52. package/dist/env-flags.d.ts +0 -1
  53. package/dist/env-flags.js +0 -4
  54. package/dist/mcp/handlers/v2-resolve-refs-envelope.d.ts +0 -5
  55. package/dist/mcp/handlers/v2-resolve-refs-envelope.js +0 -17
@@ -2,6 +2,6 @@ import { z } from 'zod';
2
2
  import type { ToolContext, ToolResult } from './types.js';
3
3
  import type { PreValidateResult } from './validation/workflow-next-prevalidate.js';
4
4
  import type { WrappedToolHandler, McpCallToolResult } from './types/workflow-tool-edition.js';
5
- export declare function toMcpResult<T>(result: ToolResult<T>): McpCallToolResult;
5
+ export declare function toMcpResult<T>(result: ToolResult<T>, ctx?: ToolContext): McpCallToolResult;
6
6
  export declare function createHandler<TInput extends z.ZodType, TOutput>(schema: TInput, handler: (input: z.infer<TInput>, ctx: ToolContext) => Promise<ToolResult<TOutput>>, shapeSchema?: z.ZodObject<z.ZodRawShape>, aliasMap?: Readonly<Record<string, string>>): WrappedToolHandler;
7
7
  export declare function createValidatingHandler<TInput extends z.ZodType, TOutput>(schema: TInput, preValidate: (args: unknown) => PreValidateResult, handler: (input: z.infer<TInput>, ctx: ToolContext) => Promise<ToolResult<TOutput>>, shapeSchema?: z.ZodObject<z.ZodRawShape>, aliasMap?: Readonly<Record<string, string>>): WrappedToolHandler;
@@ -11,11 +11,12 @@ const v2_execution_helpers_js_1 = require("./handlers/v2-execution-helpers.js");
11
11
  const v2_response_formatter_js_1 = require("./v2-response-formatter.js");
12
12
  const render_envelope_js_1 = require("./render-envelope.js");
13
13
  const jsonResponsesOverride = process.env.WORKRAIL_JSON_RESPONSES === 'true';
14
- function toMcpResult(result) {
14
+ function toMcpResult(result, ctx) {
15
15
  switch (result.type) {
16
16
  case 'success': {
17
+ const cleanResponseFormat = ctx?.featureFlags.isEnabled('cleanResponseFormat') ?? false;
17
18
  if (!jsonResponsesOverride) {
18
- const formatted = (0, v2_response_formatter_js_1.formatV2ExecutionResponse)(result.data) ?? (0, v2_response_formatter_js_1.formatV2ResumeResponse)(result.data);
19
+ const formatted = (0, v2_response_formatter_js_1.formatV2ExecutionResponse)(result.data, cleanResponseFormat) ?? (0, v2_response_formatter_js_1.formatV2ResumeResponse)(result.data);
19
20
  if (formatted !== null) {
20
21
  const content = [{ type: 'text', text: formatted.primary }];
21
22
  if (formatted.references != null) {
@@ -76,14 +77,14 @@ function createHandler(schema, handler, shapeSchema, aliasMap) {
76
77
  message: e.message,
77
78
  })),
78
79
  ...patchedDetails,
79
- }));
80
+ }), ctx);
80
81
  }
81
82
  try {
82
- return toMcpResult(await handler(parseResult.data, ctx));
83
+ return toMcpResult(await handler(parseResult.data, ctx), ctx);
83
84
  }
84
85
  catch (err) {
85
86
  console.error('[WorkRail] Unhandled exception in tool handler:', err);
86
- return toMcpResult((0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail encountered an unexpected error. This is not caused by your input.', { suggestion: (0, v2_execution_helpers_js_1.internalSuggestion)('Retry the call.', 'WorkRail has an internal error.') }));
87
+ return toMcpResult((0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail encountered an unexpected error. This is not caused by your input.', { suggestion: (0, v2_execution_helpers_js_1.internalSuggestion)('Retry the call.', 'WorkRail has an internal error.') }), ctx);
87
88
  }
88
89
  };
89
90
  }
@@ -109,9 +110,9 @@ function createValidatingHandler(schema, preValidate, handler, shapeSchema, alia
109
110
  ...error,
110
111
  details: boundedDetails,
111
112
  };
112
- return toMcpResult(boundedError);
113
+ return toMcpResult(boundedError, ctx);
113
114
  }
114
- return toMcpResult(error);
115
+ return toMcpResult(error, ctx);
115
116
  }
116
117
  return innerHandler(normalizedArgs, ctx);
117
118
  };
@@ -2,6 +2,7 @@ import type { IWorkflowReader } from '../../../types/storage.js';
2
2
  import type { IFeatureFlagProvider } from '../../../config/feature-flags.js';
3
3
  import type { RememberedRootsStorePortV2 } from '../../../v2/ports/remembered-roots-store.port.js';
4
4
  import type { ManagedSourceRecordV2, ManagedSourceStorePortV2 } from '../../../v2/ports/managed-source-store.port.js';
5
+ export declare function clearWalkCacheForTesting(): void;
5
6
  export interface RequestWorkflowReaderOptions {
6
7
  readonly featureFlags: IFeatureFlagProvider;
7
8
  readonly workspacePath?: string;
@@ -3,6 +3,7 @@ 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.clearWalkCacheForTesting = clearWalkCacheForTesting;
6
7
  exports.hasRequestWorkspaceSignal = hasRequestWorkspaceSignal;
7
8
  exports.resolveRequestWorkspaceDirectory = resolveRequestWorkspaceDirectory;
8
9
  exports.toProjectWorkflowDirectory = toProjectWorkflowDirectory;
@@ -13,6 +14,27 @@ const path_1 = __importDefault(require("path"));
13
14
  const url_1 = require("url");
14
15
  const enhanced_multi_source_workflow_storage_js_1 = require("../../../infrastructure/storage/enhanced-multi-source-workflow-storage.js");
15
16
  const schema_validating_workflow_storage_js_1 = require("../../../infrastructure/storage/schema-validating-workflow-storage.js");
17
+ const with_timeout_js_1 = require("./with-timeout.js");
18
+ const SKIP_DIRS = new Set([
19
+ '.git', 'node_modules',
20
+ 'build', 'dist', 'out', 'target',
21
+ '.gradle', '.gradle-cache', '.cache',
22
+ 'DerivedData', 'Pods',
23
+ 'vendor',
24
+ '__pycache__', '.venv', 'venv',
25
+ '.next', '.nuxt', '.turbo', '.parcel-cache',
26
+ '.claude', '.claude-worktrees', '.firebender',
27
+ 'coverage', '.nyc_output',
28
+ ]);
29
+ const MAX_WALK_DEPTH = 5;
30
+ const WALK_CACHE_TTL_MS = 300000;
31
+ const walkCache = new Map();
32
+ const walkInFlight = new Map();
33
+ function clearWalkCacheForTesting() {
34
+ walkCache.clear();
35
+ walkInFlight.clear();
36
+ }
37
+ const DISCOVERY_TIMEOUT_MS = 10000;
16
38
  function hasRequestWorkspaceSignal(options) {
17
39
  return Boolean(options.workspacePath) || (options.resolvedRootUris?.length ?? 0) > 0;
18
40
  }
@@ -34,18 +56,38 @@ function toProjectWorkflowDirectory(workspaceDirectory) {
34
56
  ? workspaceDirectory
35
57
  : path_1.default.join(workspaceDirectory, 'workflows');
36
58
  }
37
- async function discoverRootedWorkflowDirectories(roots) {
59
+ function discoverRootedWorkflowDirectories(roots) {
60
+ const cacheKey = roots.map((r) => path_1.default.resolve(r)).sort().join('\0');
61
+ const now = Date.now();
62
+ const cached = walkCache.get(cacheKey);
63
+ if (cached && cached.expiresAt > now) {
64
+ return Promise.resolve(cached.result);
65
+ }
66
+ const inFlight = walkInFlight.get(cacheKey);
67
+ if (inFlight)
68
+ return inFlight;
69
+ const promise = _doWalk(cacheKey, roots, now);
70
+ walkInFlight.set(cacheKey, promise);
71
+ promise.then(() => walkInFlight.delete(cacheKey), () => walkInFlight.delete(cacheKey));
72
+ return promise;
73
+ }
74
+ async function _doWalk(cacheKey, roots, now) {
38
75
  const discoveredByPath = new Set();
39
76
  const discoveredPaths = [];
40
77
  const stalePaths = [];
41
- for (const root of roots) {
42
- const rootPath = path_1.default.resolve(root);
43
- const result = await discoverWorkflowDirectoriesUnderRoot(rootPath);
44
- if (result.stale) {
78
+ const resolvedRoots = roots.map((r) => path_1.default.resolve(r));
79
+ const rootResults = await Promise.allSettled(resolvedRoots.map((rootPath) => discoverWorkflowDirectoriesUnderRoot(rootPath)));
80
+ for (let i = 0; i < resolvedRoots.length; i++) {
81
+ const rootPath = resolvedRoots[i];
82
+ const rootResult = rootResults[i];
83
+ if (rootResult.status === 'rejected') {
84
+ throw rootResult.reason;
85
+ }
86
+ if (rootResult.value.stale) {
45
87
  stalePaths.push(rootPath);
46
88
  continue;
47
89
  }
48
- for (const nextPath of result.discovered) {
90
+ for (const nextPath of rootResult.value.discovered) {
49
91
  const normalizedPath = path_1.default.resolve(nextPath);
50
92
  if (discoveredByPath.has(normalizedPath))
51
93
  continue;
@@ -53,13 +95,22 @@ async function discoverRootedWorkflowDirectories(roots) {
53
95
  discoveredPaths.push(normalizedPath);
54
96
  }
55
97
  }
56
- return { discovered: discoveredPaths, stale: stalePaths };
98
+ const result = { discovered: discoveredPaths, stale: stalePaths };
99
+ walkCache.set(cacheKey, { result, expiresAt: now + WALK_CACHE_TTL_MS });
100
+ return result;
57
101
  }
58
102
  async function createWorkflowReaderForRequest(options) {
59
103
  const workspaceDirectory = resolveRequestWorkspaceDirectory(options);
60
104
  const projectWorkflowDirectory = toProjectWorkflowDirectory(workspaceDirectory);
61
105
  const rememberedRoots = await listRememberedRoots(options.rememberedRootsStore);
62
- const { discovered: rootedWorkflowDirectories, stale: stalePaths } = await discoverRootedWorkflowDirectories(rememberedRoots);
106
+ let discoveryResult;
107
+ try {
108
+ discoveryResult = await (0, with_timeout_js_1.withTimeout)(discoverRootedWorkflowDirectories(rememberedRoots), DISCOVERY_TIMEOUT_MS, 'workflow_root_discovery');
109
+ }
110
+ catch {
111
+ discoveryResult = { discovered: [], stale: [] };
112
+ }
113
+ const { discovered: rootedWorkflowDirectories, stale: stalePaths } = discoveryResult;
63
114
  const rootedCustomPaths = rootedWorkflowDirectories.filter((directory) => directory !== projectWorkflowDirectory);
64
115
  const { records: allManagedRecords, storeError: managedStoreError } = await listManagedSourceRecords(options.managedSourceStore);
65
116
  const envCustomPaths = parseWorkflowStoragePathEnv();
@@ -70,17 +121,30 @@ async function createWorkflowReaderForRequest(options) {
70
121
  const additionalManagedPaths = [];
71
122
  const activeManagedRecords = [];
72
123
  const staleManagedRecords = [];
124
+ const alreadyCovered = [];
125
+ const needsStatCheck = [];
73
126
  for (const record of allManagedRecords) {
74
127
  if (normalizedCustom.has(path_1.default.resolve(record.path))) {
75
- activeManagedRecords.push(record);
76
- continue;
77
- }
78
- if (await isDirectory(record.path)) {
79
- additionalManagedPaths.push(record.path);
80
- activeManagedRecords.push(record);
128
+ alreadyCovered.push(record);
81
129
  }
82
130
  else {
83
- staleManagedRecords.push(record);
131
+ needsStatCheck.push(record);
132
+ }
133
+ }
134
+ activeManagedRecords.push(...alreadyCovered);
135
+ if (needsStatCheck.length > 0) {
136
+ const statResults = await (0, with_timeout_js_1.withTimeout)(Promise.allSettled(needsStatCheck.map((record) => isDirectory(record.path))), DISCOVERY_TIMEOUT_MS, 'managed_source_stat').catch(() => null);
137
+ for (let i = 0; i < needsStatCheck.length; i++) {
138
+ const record = needsStatCheck[i];
139
+ const result = statResults?.[i];
140
+ const isDir = result?.status === 'fulfilled' && result.value === true;
141
+ if (isDir) {
142
+ additionalManagedPaths.push(record.path);
143
+ activeManagedRecords.push(record);
144
+ }
145
+ else {
146
+ staleManagedRecords.push(record);
147
+ }
84
148
  }
85
149
  }
86
150
  const customPaths = [...rootedCustomPaths, ...additionalManagedPaths];
@@ -118,8 +182,8 @@ async function listRememberedRoots(rememberedRootsStore) {
118
182
  return [];
119
183
  const result = await rememberedRootsStore.listRoots();
120
184
  if (result.isErr()) {
121
- const error = result.error;
122
- throw new Error(`Failed to load remembered workflow roots: ${error.code}: ${error.message}`);
185
+ console.error(`[workrail] Failed to load remembered workflow roots: ${result.error.code}: ${result.error.message}`);
186
+ return [];
123
187
  }
124
188
  return result.value.map((root) => path_1.default.resolve(root));
125
189
  }
@@ -136,7 +200,7 @@ async function discoverWorkflowDirectoriesUnderRoot(rootPath) {
136
200
  }
137
201
  return { discovered: discoveredPaths, stale: false };
138
202
  }
139
- async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPaths) {
203
+ async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPaths, depth = 0) {
140
204
  const entries = await promises_1.default.readdir(currentDirectory, { withFileTypes: true });
141
205
  const sortedEntries = [...entries].sort((a, b) => a.name.localeCompare(b.name));
142
206
  for (const entry of sortedEntries) {
@@ -152,14 +216,20 @@ async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPath
152
216
  }
153
217
  continue;
154
218
  }
155
- await walkForRootedWorkflowDirectories(entryPath, discoveredPaths).catch((err) => {
219
+ if (depth >= MAX_WALK_DEPTH) {
220
+ if (process.env['WORKRAIL_DEV'] === '1') {
221
+ console.error(`[workrail] walk depth limit (${MAX_WALK_DEPTH}) reached at: ${entryPath}`);
222
+ }
223
+ continue;
224
+ }
225
+ await walkForRootedWorkflowDirectories(entryPath, discoveredPaths, depth + 1).catch((err) => {
156
226
  if (err.code !== 'ENOENT')
157
227
  throw err;
158
228
  });
159
229
  }
160
230
  }
161
231
  function shouldSkipDirectory(name) {
162
- return name === '.git' || name === 'node_modules';
232
+ return SKIP_DIRS.has(name);
163
233
  }
164
234
  async function isDirectory(targetPath) {
165
235
  try {
@@ -28,4 +28,5 @@ export declare function handleAdvanceIntent(args: {
28
28
  readonly sha256: Sha256PortV2;
29
29
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
30
30
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
31
+ readonly cleanResponseFormat?: boolean;
31
32
  }): RA<z.infer<typeof V2ContinueWorkflowOutputSchema>, ContinueWorkflowError>;
@@ -11,7 +11,7 @@ const advance_js_1 = require("./advance.js");
11
11
  const sorted_event_log_js_1 = require("../../../v2/durable-core/sorted-event-log.js");
12
12
  const session_index_js_1 = require("../../../v2/durable-core/session-index.js");
13
13
  function handleAdvanceIntent(args) {
14
- const { input, sessionId, runId, nodeId, attemptId, workflowHashRef, truth, gate, sessionStore, snapshotStore, pinnedStore, tokenCodecPorts, idFactory, sha256, aliasStore, entropy } = args;
14
+ const { input, sessionId, runId, nodeId, attemptId, workflowHashRef, truth, gate, sessionStore, snapshotStore, pinnedStore, tokenCodecPorts, idFactory, sha256, aliasStore, entropy, cleanResponseFormat } = args;
15
15
  const dedupeKey = `advance_recorded:${sessionId}:${nodeId}:${attemptId}`;
16
16
  const preLockSortedResult = (0, sorted_event_log_js_1.asSortedEventLog)(truth.events);
17
17
  if (preLockSortedResult.isErr()) {
@@ -101,6 +101,7 @@ function handleAdvanceIntent(args) {
101
101
  tokenCodecPorts,
102
102
  aliasStore,
103
103
  entropy,
104
+ cleanResponseFormat,
104
105
  });
105
106
  }
106
107
  return gate
@@ -198,6 +199,7 @@ function handleAdvanceIntent(args) {
198
199
  tokenCodecPorts,
199
200
  aliasStore,
200
201
  entropy,
202
+ cleanResponseFormat,
201
203
  });
202
204
  });
203
205
  });
@@ -27,4 +27,5 @@ export declare function handleRehydrateIntent(args: {
27
27
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
28
28
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
29
29
  readonly resolvedRootUris?: readonly string[];
30
+ readonly cleanResponseFormat?: boolean;
30
31
  }): RA<RehydrateResult, ContinueWorkflowError>;
@@ -20,7 +20,7 @@ const index_js_2 = require("./index.js");
20
20
  const step_content_envelope_js_1 = require("../../step-content-envelope.js");
21
21
  const assert_output_js_1 = require("../../assert-output.js");
22
22
  function handleRehydrateIntent(args) {
23
- const { input, sessionId, runId, nodeId, workflowHashRef, truth, tokenCodecPorts, pinnedStore, snapshotStore, idFactory, aliasStore, entropy, resolvedRootUris } = args;
23
+ const { input, sessionId, runId, nodeId, workflowHashRef, truth, tokenCodecPorts, pinnedStore, snapshotStore, idFactory, aliasStore, entropy, resolvedRootUris, cleanResponseFormat } = args;
24
24
  const runStarted = truth.events.find((e) => e.kind === constants_js_1.EVENT_KIND.RUN_STARTED && e.scope.runId === String(runId));
25
25
  const workflowId = runStarted?.data.workflowId;
26
26
  if (!runStarted || typeof workflowId !== 'string' || workflowId.trim() === '') {
@@ -124,6 +124,7 @@ function handleRehydrateIntent(args) {
124
124
  runId: (0, index_js_1.asRunId)(String(runId)),
125
125
  nodeId: (0, index_js_1.asNodeId)(String(nodeId)),
126
126
  rehydrateOnly: true,
127
+ cleanResponseFormat,
127
128
  });
128
129
  if (metaRes.isErr()) {
129
130
  return (0, neverthrow_1.errAsync)({
@@ -75,6 +75,7 @@ function loadAndRehydrate(args) {
75
75
  aliasStore: tokenAliasStore,
76
76
  entropy,
77
77
  resolvedRootUris: args.ctx.v2.resolvedRootUris,
78
+ cleanResponseFormat: args.ctx.featureFlags?.isEnabled('cleanResponseFormat') ?? false,
78
79
  }));
79
80
  }
80
81
  function executeContinueWorkflow(input, ctx) {
@@ -140,6 +141,7 @@ function executeContinueWorkflow(input, ctx) {
140
141
  sha256,
141
142
  aliasStore: tokenAliasStore,
142
143
  entropy,
144
+ cleanResponseFormat: ctx.featureFlags?.isEnabled('cleanResponseFormat') ?? false,
143
145
  }))
144
146
  .map((response) => ({ response }));
145
147
  });
@@ -25,6 +25,7 @@ export declare function buildAdvancedReplayResponse(args: {
25
25
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
26
26
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
27
27
  readonly precomputedIndex?: import('../../../v2/durable-core/session-index.js').SessionIndex;
28
+ readonly cleanResponseFormat?: boolean;
28
29
  }): RA<z.infer<typeof V2ContinueWorkflowOutputSchema>, ContinueWorkflowError>;
29
30
  export declare function replayFromRecordedAdvance(args: {
30
31
  readonly recordedEvent: Extract<DomainEventV1, {
@@ -43,4 +44,5 @@ export declare function replayFromRecordedAdvance(args: {
43
44
  readonly aliasStore: import('../../../v2/ports/token-alias-store.port.js').TokenAliasStorePortV2;
44
45
  readonly entropy: import('../../../v2/ports/random-entropy.port.js').RandomEntropyPortV2;
45
46
  readonly precomputedIndex?: import('../../../v2/durable-core/session-index.js').SessionIndex;
47
+ readonly cleanResponseFormat?: boolean;
46
48
  }): RA<z.infer<typeof V2ContinueWorkflowOutputSchema>, ContinueWorkflowError>;
@@ -78,6 +78,7 @@ function buildAdvancedReplayResponse(args) {
78
78
  nodeId: (0, index_js_1.asNodeId)(String(toNodeIdBranded)),
79
79
  rehydrateOnly: false,
80
80
  precomputedIndex: args.precomputedIndex,
81
+ cleanResponseFormat: args.cleanResponseFormat,
81
82
  });
82
83
  if (result.isErr()) {
83
84
  return (0, neverthrow_1.errAsync)({ kind: 'prompt_render_failed', message: result.error.message });
@@ -116,6 +117,7 @@ function buildAdvancedReplayResponse(args) {
116
117
  nodeId: (0, index_js_1.asNodeId)(String(toNodeIdBranded)),
117
118
  rehydrateOnly: false,
118
119
  precomputedIndex: args.precomputedIndex,
120
+ cleanResponseFormat: args.cleanResponseFormat,
119
121
  });
120
122
  if (result.isErr()) {
121
123
  return (0, neverthrow_1.errAsync)({ kind: 'prompt_render_failed', message: result.error.message });
@@ -208,6 +210,7 @@ function replayFromRecordedAdvance(args) {
208
210
  aliasStore,
209
211
  entropy,
210
212
  precomputedIndex: args.precomputedIndex,
213
+ cleanResponseFormat: args.cleanResponseFormat,
211
214
  });
212
215
  });
213
216
  }
@@ -26,6 +26,8 @@ const request_workflow_reader_js_1 = require("../shared/request-workflow-reader.
26
26
  const step_content_envelope_js_1 = require("../../step-content-envelope.js");
27
27
  const v2_workspace_resolution_js_2 = require("../v2-workspace-resolution.js");
28
28
  const v2_reference_resolver_js_1 = require("../v2-reference-resolver.js");
29
+ const with_timeout_js_1 = require("../shared/with-timeout.js");
30
+ const REFERENCE_RESOLUTION_TIMEOUT_MS = 5000;
29
31
  function loadAndPinWorkflow(args) {
30
32
  const { workflowId, workflowReader, crypto, pinnedStore, validationPipelineDeps, workspacePath, resolvedRootUris } = args;
31
33
  return neverthrow_1.ResultAsync.fromPromise(workflowReader.getWorkflowById(workflowId), (e) => ({
@@ -77,7 +79,7 @@ function loadAndPinWorkflow(args) {
77
79
  }
78
80
  return pinnedStore.put(workflowHash, enrichedCompiled)
79
81
  .mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
80
- .andThen(() => pinnedStore.get(workflowHash).mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause })));
82
+ .map(() => enrichedCompiled);
81
83
  })
82
84
  .andThen((pinned) => {
83
85
  if (!pinned || pinned.sourceKind !== 'v1_pinned' || !(0, workflow_definition_js_1.hasWorkflowDefinitionShape)(pinned.definition)) {
@@ -239,10 +241,11 @@ function executeStartWorkflow(input, ctx) {
239
241
  workspacePath: input.workspacePath,
240
242
  resolvedRootUris: ctx.v2.resolvedRootUris,
241
243
  rememberedRootsStore: ctx.v2.rememberedRootsStore,
244
+ managedSourceStore: ctx.v2.managedSourceStore,
242
245
  }), (err) => ({
243
246
  kind: 'precondition_failed',
244
247
  message: `Failed to initialize workflow reader: ${String(err)}`,
245
- })).map(({ reader, stalePaths }) => ({
248
+ })).map(({ reader, stalePaths, managedStoreError }) => ({
246
249
  workflowReader: {
247
250
  getWorkflowById: async (workflowId) => {
248
251
  const requestResult = await reader.getWorkflowById(workflowId);
@@ -252,21 +255,24 @@ function executeStartWorkflow(input, ctx) {
252
255
  },
253
256
  },
254
257
  stalePaths,
258
+ managedStoreError,
255
259
  }))
256
- : (0, neverthrow_1.okAsync)({ workflowReader: ctx.workflowService, stalePaths: [] });
257
- return readerRA.andThen(({ workflowReader, stalePaths }) => loadAndPinWorkflow({
258
- workflowId: input.workflowId,
259
- workflowReader,
260
- crypto,
261
- pinnedStore,
262
- validationPipelineDeps,
263
- workspacePath: input.workspacePath,
264
- resolvedRootUris: ctx.v2.resolvedRootUris,
265
- })
266
- .andThen(({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences }) => {
267
- const anchorsRA = (0, v2_workspace_resolution_js_1.resolveWorkspaceAnchors)(ctx.v2, input.workspacePath)
268
- .map((anchors) => (0, observation_builder_js_1.anchorsToObservations)(anchors));
269
- return anchorsRA.andThen((observations) => {
260
+ : (0, neverthrow_1.okAsync)({ workflowReader: ctx.workflowService, stalePaths: [], managedStoreError: undefined });
261
+ const anchorsRA = (0, v2_workspace_resolution_js_1.resolveWorkspaceAnchors)(ctx.v2, input.workspacePath)
262
+ .map((anchors) => (0, observation_builder_js_1.anchorsToObservations)(anchors))
263
+ .mapErr((x) => x);
264
+ return readerRA.andThen(({ workflowReader, stalePaths, managedStoreError }) => {
265
+ const pinnedRA = loadAndPinWorkflow({
266
+ workflowId: input.workflowId,
267
+ workflowReader,
268
+ crypto,
269
+ pinnedStore,
270
+ validationPipelineDeps,
271
+ workspacePath: input.workspacePath,
272
+ resolvedRootUris: ctx.v2.resolvedRootUris,
273
+ });
274
+ return neverthrow_1.ResultAsync.combine([pinnedRA, anchorsRA])
275
+ .andThen(([{ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences }, observations]) => {
270
276
  const sessionId = idFactory.mintSessionId();
271
277
  const runId = idFactory.mintRunId();
272
278
  const nodeId = idFactory.mintNodeId();
@@ -314,11 +320,11 @@ function executeStartWorkflow(input, ctx) {
314
320
  snapshotPins: [{ snapshotRef, eventIndex: 2, createdByEventId: events[2].eventId }],
315
321
  }, emptyTruth))
316
322
  .mapErr((cause) => ({ kind: 'session_append_failed', cause }))
317
- .map(() => ({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences, sessionId, runId, nodeId }));
323
+ .map(() => ({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences, sessionId, runId, nodeId, stalePaths, managedStoreError }));
318
324
  });
319
325
  });
320
326
  })
321
- .andThen(({ pinnedWorkflow, firstStep, workflowHash, sessionId, runId, nodeId, resolvedReferences }) => {
327
+ .andThen(({ pinnedWorkflow, firstStep, workflowHash, sessionId, runId, nodeId, resolvedReferences, stalePaths, managedStoreError }) => {
322
328
  const wfRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
323
329
  if (wfRefRes.isErr()) {
324
330
  return (0, neverthrow_1.errAsync)({
@@ -346,6 +352,7 @@ function executeStartWorkflow(input, ctx) {
346
352
  runId: (0, index_js_1.asRunId)(String(runId)),
347
353
  nodeId: (0, index_js_1.asNodeId)(String(nodeId)),
348
354
  rehydrateOnly: false,
355
+ cleanResponseFormat: ctx.featureFlags?.isEnabled('cleanResponseFormat') ?? false,
349
356
  });
350
357
  if (metaResult.isErr()) {
351
358
  return (0, neverthrow_1.errAsync)({
@@ -361,6 +368,9 @@ function executeStartWorkflow(input, ctx) {
361
368
  meta,
362
369
  references: resolvedReferences,
363
370
  });
371
+ const startWarnings = managedStoreError !== undefined
372
+ ? [`Managed workflow source store was temporarily unavailable (${managedStoreError}). Managed sources were not loaded.`]
373
+ : undefined;
364
374
  const parsed = {
365
375
  continueToken: tokens.continueToken,
366
376
  checkpointToken: tokens.checkpointToken,
@@ -370,16 +380,34 @@ function executeStartWorkflow(input, ctx) {
370
380
  nextIntent,
371
381
  nextCall: (0, index_js_2.buildNextCall)({ continueToken: tokens.continueToken, isComplete: false, pending }),
372
382
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
383
+ ...(startWarnings !== undefined ? { warnings: startWarnings } : {}),
373
384
  };
374
385
  return (0, neverthrow_1.okAsync)({ response: parsed, contentEnvelope });
375
386
  });
376
- }));
387
+ });
377
388
  }
378
389
  function enrichPinnedSnapshotWithResolvedReferences(snapshot, references, workspacePath) {
379
390
  if (references.length === 0) {
380
391
  return (0, neverthrow_1.okAsync)({ snapshot, resolvedReferences: [] });
381
392
  }
382
- return neverthrow_1.ResultAsync.fromPromise((0, v2_reference_resolver_js_1.resolveWorkflowReferences)(references, workspacePath), () => ({ kind: 'reference_resolution_failed' })).map((result) => {
393
+ const allUnresolved = references.map((ref) => ({
394
+ id: ref.id,
395
+ title: ref.title,
396
+ source: ref.source,
397
+ purpose: ref.purpose,
398
+ authoritative: ref.authoritative,
399
+ resolveFrom: (ref.resolveFrom ?? 'workspace'),
400
+ status: 'unresolved',
401
+ }));
402
+ const resolutionPromise = (0, with_timeout_js_1.withTimeout)((0, v2_reference_resolver_js_1.resolveWorkflowReferences)(references, workspacePath), REFERENCE_RESOLUTION_TIMEOUT_MS, 'reference_resolution').catch(() => null);
403
+ return neverthrow_1.ResultAsync.fromPromise(resolutionPromise, () => ({ kind: 'reference_resolution_failed' })).map((result) => {
404
+ if (result === null) {
405
+ console.warn('[workrail:reference-resolution] timed out; all references marked unresolved');
406
+ return {
407
+ snapshot: { ...snapshot, resolvedReferences: [...allUnresolved] },
408
+ resolvedReferences: allUnresolved,
409
+ };
410
+ }
383
411
  for (const warning of result.warnings) {
384
412
  console.warn(`[workrail:reference-resolution] ${warning.message}`);
385
413
  }
@@ -15,6 +15,7 @@ const neverthrow_1 = require("neverthrow");
15
15
  const types_js_1 = require("../types.js");
16
16
  const error_mapper_js_1 = require("../error-mapper.js");
17
17
  const v2_execution_helpers_js_1 = require("./v2-execution-helpers.js");
18
+ const assert_never_js_1 = require("../../runtime/assert-never.js");
18
19
  const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
19
20
  const hashing_js_1 = require("../../v2/durable-core/canonical/hashing.js");
20
21
  const TIMEOUT_MS = 30000;
@@ -35,8 +36,7 @@ function readCurrentSpecVersion() {
35
36
  }
36
37
  }
37
38
  const CURRENT_SPEC_VERSION = readCurrentSpecVersion();
38
- const DEV_STALENESS = process.env['WORKRAIL_DEV_STALENESS'] === '1';
39
- function shouldShowStaleness(category, devMode = DEV_STALENESS) {
39
+ function shouldShowStaleness(category, devMode = (0, dev_mode_js_1.isDevMode)()) {
40
40
  if (devMode)
41
41
  return true;
42
42
  return category === 'personal' || category === 'rooted_sharing' || category === 'external';
@@ -96,6 +96,7 @@ function computeWorkflowStaleness(stamp, currentVersion) {
96
96
  specVersionAtLastReview: stamp,
97
97
  };
98
98
  }
99
+ const dev_mode_js_1 = require("../dev-mode.js");
99
100
  const with_timeout_js_1 = require("./shared/with-timeout.js");
100
101
  const request_workflow_reader_js_1 = require("./shared/request-workflow-reader.js");
101
102
  const remembered_roots_js_1 = require("./shared/remembered-roots.js");
@@ -443,6 +444,7 @@ function deriveSourceCatalogEntry(options) {
443
444
  case 'plugin':
444
445
  return { sourceKey, category: 'external', source: { kind: source.kind, displayName }, sourceMode: 'live_directory', effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed };
445
446
  }
447
+ (0, assert_never_js_1.assertNever)(source);
446
448
  }
447
449
  function deriveSourceKey(source) {
448
450
  switch (source.kind) {
@@ -2443,6 +2443,7 @@ export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
2443
2443
  tool: "continue_workflow";
2444
2444
  }>>;
2445
2445
  staleRoots: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
2446
+ warnings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
2446
2447
  }, "strip", z.ZodTypeAny, {
2447
2448
  pending: {
2448
2449
  stepId: string;
@@ -2462,6 +2463,7 @@ export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
2462
2463
  };
2463
2464
  tool: "continue_workflow";
2464
2465
  } | null;
2466
+ warnings?: string[] | undefined;
2465
2467
  continueToken?: string | undefined;
2466
2468
  checkpointToken?: string | undefined;
2467
2469
  staleRoots?: string[] | undefined;
@@ -2484,6 +2486,7 @@ export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
2484
2486
  };
2485
2487
  tool: "continue_workflow";
2486
2488
  } | null;
2489
+ warnings?: string[] | undefined;
2487
2490
  continueToken?: string | undefined;
2488
2491
  checkpointToken?: string | undefined;
2489
2492
  staleRoots?: string[] | undefined;
@@ -2506,6 +2509,7 @@ export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
2506
2509
  };
2507
2510
  tool: "continue_workflow";
2508
2511
  } | null;
2512
+ warnings?: string[] | undefined;
2509
2513
  continueToken?: string | undefined;
2510
2514
  checkpointToken?: string | undefined;
2511
2515
  staleRoots?: string[] | undefined;
@@ -2528,6 +2532,7 @@ export declare const V2StartWorkflowOutputSchema: z.ZodEffects<z.ZodObject<{
2528
2532
  };
2529
2533
  tool: "continue_workflow";
2530
2534
  } | null;
2535
+ warnings?: string[] | undefined;
2531
2536
  continueToken?: string | undefined;
2532
2537
  checkpointToken?: string | undefined;
2533
2538
  staleRoots?: string[] | undefined;
@@ -392,6 +392,8 @@ exports.V2StartWorkflowOutputSchema = zod_1.z.object({
392
392
  staleRoots: zod_1.z.array(zod_1.z.string()).optional().describe('Workflow source paths that were inaccessible during discovery (missing remembered roots or missing managed source directories). ' +
393
393
  'Workflows from these paths were not included in this response. ' +
394
394
  'These paths will be rechecked on the next call.'),
395
+ warnings: zod_1.z.array(zod_1.z.string()).optional().describe('Non-fatal warnings about workflow source availability. ' +
396
+ 'The workflow was started successfully but some sources may have been unavailable (e.g., managed source store temporarily inaccessible).'),
395
397
  }).refine((data) => (data.pending ? data.continueToken != null : true), { message: 'continueToken is required when a pending step exists' });
396
398
  exports.CreateSessionOutputSchema = zod_1.z.object({
397
399
  sessionId: zod_1.z.string().min(1),
@@ -217,10 +217,11 @@ async function composeServer() {
217
217
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
218
218
  tools,
219
219
  }));
220
- const timingSink = dev_mode_js_1.DEV_MODE
220
+ const devMode = (0, dev_mode_js_1.isDevMode)();
221
+ const timingSink = devMode
221
222
  ? (0, tool_call_timing_js_1.composeSinks)((0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer), (0, tool_call_timing_js_1.createDevPerfSink)())
222
223
  : (0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer);
223
- if (dev_mode_js_1.DEV_MODE) {
224
+ if (devMode) {
224
225
  console.error('[PerfTrace] WORKRAIL_DEV=1 -- tool call timing active');
225
226
  }
226
227
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -8,5 +8,5 @@ export interface FormattedResponse {
8
8
  readonly references?: FormattedReferences;
9
9
  readonly supplements?: readonly FormattedSupplement[];
10
10
  }
11
- export declare function formatV2ExecutionResponse(data: unknown): FormattedResponse | null;
11
+ export declare function formatV2ExecutionResponse(data: unknown, cleanResponseFormat: boolean): FormattedResponse | null;
12
12
  export declare function formatV2ResumeResponse(data: unknown): FormattedResponse | null;
@@ -4,7 +4,6 @@ exports.formatV2ExecutionResponse = formatV2ExecutionResponse;
4
4
  exports.formatV2ResumeResponse = formatV2ResumeResponse;
5
5
  const render_envelope_js_1 = require("./render-envelope.js");
6
6
  const response_supplements_js_1 = require("./response-supplements.js");
7
- const env_flags_js_1 = require("../env-flags.js");
8
7
  function isV2ExecutionResponse(data) {
9
8
  if (typeof data !== 'object' || data === null)
10
9
  return false;
@@ -348,11 +347,11 @@ function renderReferencesSection(contentEnvelope, lifecycle) {
348
347
  return null;
349
348
  }
350
349
  }
351
- function formatV2ExecutionResponse(data) {
350
+ function formatV2ExecutionResponse(data, cleanResponseFormat) {
352
351
  const renderInput = deriveRenderInput(data);
353
352
  if (!renderInput)
354
353
  return null;
355
- const cleanFormat = env_flags_js_1.CLEAN_RESPONSE_FORMAT;
354
+ const cleanFormat = cleanResponseFormat;
356
355
  const { response, lifecycle, contentEnvelope } = renderInput;
357
356
  const references = renderReferencesSection(contentEnvelope, lifecycle);
358
357
  if (cleanFormat) {