@exaudeus/workrail 3.5.0 → 3.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/application/services/validation-engine.js +50 -0
- package/dist/config/feature-flags.js +8 -0
- package/dist/engine/engine-factory.js +4 -2
- package/dist/manifest.json +102 -54
- package/dist/mcp/handler-factory.js +21 -4
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +6 -1
- package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +22 -4
- package/dist/mcp/handlers/v2-execution/index.d.ts +6 -1
- package/dist/mcp/handlers/v2-execution/index.js +13 -3
- package/dist/mcp/handlers/v2-execution/start.d.ts +9 -1
- package/dist/mcp/handlers/v2-execution/start.js +74 -36
- package/dist/mcp/handlers/v2-execution-helpers.d.ts +2 -0
- package/dist/mcp/handlers/v2-execution-helpers.js +2 -0
- package/dist/mcp/handlers/v2-reference-resolver.d.ts +14 -0
- package/dist/mcp/handlers/v2-reference-resolver.js +112 -0
- package/dist/mcp/handlers/v2-resolve-refs-envelope.d.ts +5 -0
- package/dist/mcp/handlers/v2-resolve-refs-envelope.js +17 -0
- package/dist/mcp/handlers/v2-workflow.js +2 -0
- package/dist/mcp/output-schemas.d.ts +38 -0
- package/dist/mcp/output-schemas.js +8 -0
- package/dist/mcp/render-envelope.d.ts +21 -0
- package/dist/mcp/render-envelope.js +59 -0
- package/dist/mcp/response-supplements.d.ts +17 -0
- package/dist/mcp/response-supplements.js +58 -0
- package/dist/mcp/step-content-envelope.d.ts +32 -0
- package/dist/mcp/step-content-envelope.js +13 -0
- package/dist/mcp/transports/stdio-entry.js +19 -6
- package/dist/mcp/v2-response-formatter.d.ts +11 -1
- package/dist/mcp/v2-response-formatter.js +168 -1
- package/dist/mcp/workflow-protocol-contracts.js +9 -7
- package/dist/types/workflow-definition.d.ts +16 -0
- package/dist/types/workflow-definition.js +1 -0
- package/dist/utils/condition-evaluator.d.ts +1 -0
- package/dist/utils/condition-evaluator.js +7 -0
- package/dist/v2/durable-core/domain/context-template-resolver.d.ts +2 -0
- package/dist/v2/durable-core/domain/context-template-resolver.js +26 -0
- package/dist/v2/durable-core/domain/prompt-renderer.d.ts +2 -0
- package/dist/v2/durable-core/domain/prompt-renderer.js +93 -15
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +256 -0
- package/dist/v2/durable-core/schemas/compiled-workflow/index.js +30 -0
- package/package.json +4 -1
- package/spec/authoring-spec.provenance.json +77 -0
- package/spec/authoring-spec.schema.json +370 -0
- package/workflows/coding-task-workflow-agentic.lean.v2.json +132 -30
- package/workflows/workflow-for-workflows.json +27 -1
|
@@ -24,8 +24,11 @@ const constants_js_1 = require("../../../v2/durable-core/constants.js");
|
|
|
24
24
|
const index_js_2 = require("./index.js");
|
|
25
25
|
const start_construction_js_1 = require("../../../v2/durable-core/domain/start-construction.js");
|
|
26
26
|
const request_workflow_reader_js_1 = require("../shared/request-workflow-reader.js");
|
|
27
|
+
const step_content_envelope_js_1 = require("../../step-content-envelope.js");
|
|
28
|
+
const v2_workspace_resolution_js_2 = require("../v2-workspace-resolution.js");
|
|
29
|
+
const v2_reference_resolver_js_1 = require("../v2-reference-resolver.js");
|
|
27
30
|
function loadAndPinWorkflow(args) {
|
|
28
|
-
const { workflowId, workflowReader, crypto, pinnedStore, validationPipelineDeps } = args;
|
|
31
|
+
const { workflowId, workflowReader, crypto, pinnedStore, validationPipelineDeps, workspacePath, resolvedRootUris } = args;
|
|
29
32
|
return neverthrow_1.ResultAsync.fromPromise(workflowReader.getWorkflowById(workflowId), (e) => ({
|
|
30
33
|
kind: 'precondition_failed',
|
|
31
34
|
message: e instanceof Error ? e.message : String(e),
|
|
@@ -59,38 +62,48 @@ function loadAndPinWorkflow(args) {
|
|
|
59
62
|
});
|
|
60
63
|
}
|
|
61
64
|
const compiled = pipelineOutcome.snapshot;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
|
|
69
|
-
.andThen((existingPinned) => {
|
|
70
|
-
if (!existingPinned) {
|
|
71
|
-
return pinnedStore.put(workflowHash, compiled)
|
|
72
|
-
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }));
|
|
65
|
+
const bindingBaseDir = (0, v2_workspace_resolution_js_2.resolveBindingBaseDir)(workspacePath, resolvedRootUris ?? []);
|
|
66
|
+
return enrichPinnedSnapshotWithResolvedReferences(compiled, workflow.definition.references ?? [], bindingBaseDir)
|
|
67
|
+
.andThen(({ snapshot: enrichedCompiled, resolvedReferences }) => {
|
|
68
|
+
const workflowHashRes = (0, hashing_js_1.workflowHashForCompiledSnapshot)(enrichedCompiled, crypto);
|
|
69
|
+
if (workflowHashRes.isErr()) {
|
|
70
|
+
return (0, neverthrow_1.errAsync)({ kind: 'hash_computation_failed', message: workflowHashRes.error.message });
|
|
73
71
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
const workflowHash = workflowHashRes.value;
|
|
73
|
+
return pinnedStore.get(workflowHash)
|
|
74
|
+
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
|
|
75
|
+
.andThen((existingPinned) => {
|
|
76
|
+
if (!existingPinned) {
|
|
77
|
+
return pinnedStore.put(workflowHash, enrichedCompiled)
|
|
78
|
+
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }));
|
|
79
|
+
}
|
|
80
|
+
return (0, neverthrow_1.okAsync)(undefined);
|
|
81
|
+
})
|
|
82
|
+
.andThen(() => pinnedStore.get(workflowHash).mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause })))
|
|
83
|
+
.andThen((pinned) => {
|
|
84
|
+
if (!pinned || pinned.sourceKind !== 'v1_pinned' || !(0, workflow_definition_js_1.hasWorkflowDefinitionShape)(pinned.definition)) {
|
|
85
|
+
return (0, neverthrow_1.errAsync)({
|
|
86
|
+
kind: 'invariant_violation',
|
|
87
|
+
message: 'Failed to pin executable workflow snapshot (missing or invalid pinned workflow).',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const pinnedWorkflow = (0, workflow_js_1.createWorkflow)(pinned.definition, (0, workflow_source_js_1.createBundledSource)());
|
|
91
|
+
const resolution = (0, start_construction_js_1.resolveFirstStep)(workflow, pinned);
|
|
92
|
+
if (resolution.isErr()) {
|
|
93
|
+
const error = resolution.error.reason === 'no_steps'
|
|
94
|
+
? { kind: 'workflow_has_no_steps', workflowId: (0, index_js_1.asWorkflowId)(resolution.error.detail) }
|
|
95
|
+
: { kind: 'invariant_violation', message: resolution.error.detail };
|
|
96
|
+
return (0, neverthrow_1.errAsync)(error);
|
|
97
|
+
}
|
|
98
|
+
const firstStep = resolution.value;
|
|
99
|
+
return (0, neverthrow_1.okAsync)({
|
|
100
|
+
workflow,
|
|
101
|
+
firstStep,
|
|
102
|
+
workflowHash,
|
|
103
|
+
pinnedWorkflow,
|
|
104
|
+
resolvedReferences: pinned.resolvedReferences ?? resolvedReferences,
|
|
82
105
|
});
|
|
83
|
-
}
|
|
84
|
-
const pinnedWorkflow = (0, workflow_js_1.createWorkflow)(pinned.definition, (0, workflow_source_js_1.createBundledSource)());
|
|
85
|
-
const resolution = (0, start_construction_js_1.resolveFirstStep)(workflow, pinned);
|
|
86
|
-
if (resolution.isErr()) {
|
|
87
|
-
const error = resolution.error.reason === 'no_steps'
|
|
88
|
-
? { kind: 'workflow_has_no_steps', workflowId: (0, index_js_1.asWorkflowId)(resolution.error.detail) }
|
|
89
|
-
: { kind: 'invariant_violation', message: resolution.error.detail };
|
|
90
|
-
return (0, neverthrow_1.errAsync)(error);
|
|
91
|
-
}
|
|
92
|
-
const firstStep = resolution.value;
|
|
93
|
-
return (0, neverthrow_1.okAsync)({ workflow, firstStep, workflowHash, pinnedWorkflow });
|
|
106
|
+
});
|
|
94
107
|
});
|
|
95
108
|
});
|
|
96
109
|
}
|
|
@@ -215,8 +228,10 @@ function executeStartWorkflow(input, ctx) {
|
|
|
215
228
|
crypto,
|
|
216
229
|
pinnedStore,
|
|
217
230
|
validationPipelineDeps,
|
|
231
|
+
workspacePath: input.workspacePath,
|
|
232
|
+
resolvedRootUris: ctx.v2.resolvedRootUris,
|
|
218
233
|
})
|
|
219
|
-
.andThen(({ workflow, firstStep, workflowHash, pinnedWorkflow }) => {
|
|
234
|
+
.andThen(({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences }) => {
|
|
220
235
|
const anchorsRA = (0, v2_workspace_resolution_js_1.resolveWorkspaceAnchors)(ctx.v2, input.workspacePath)
|
|
221
236
|
.map((anchors) => (0, observation_builder_js_1.anchorsToObservations)(anchors));
|
|
222
237
|
return anchorsRA.andThen((observations) => {
|
|
@@ -265,11 +280,11 @@ function executeStartWorkflow(input, ctx) {
|
|
|
265
280
|
snapshotPins: [{ snapshotRef, eventIndex: 2, createdByEventId: events[2].eventId }],
|
|
266
281
|
}))
|
|
267
282
|
.mapErr((cause) => ({ kind: 'session_append_failed', cause }))
|
|
268
|
-
.map(() => ({ workflow, firstStep, workflowHash, pinnedWorkflow, sessionId, runId, nodeId }));
|
|
283
|
+
.map(() => ({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences, sessionId, runId, nodeId }));
|
|
269
284
|
});
|
|
270
285
|
});
|
|
271
286
|
})
|
|
272
|
-
.andThen(({ pinnedWorkflow, firstStep, workflowHash, sessionId, runId, nodeId }) => {
|
|
287
|
+
.andThen(({ pinnedWorkflow, firstStep, workflowHash, sessionId, runId, nodeId, resolvedReferences }) => {
|
|
273
288
|
const wfRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
|
|
274
289
|
if (wfRefRes.isErr()) {
|
|
275
290
|
return (0, neverthrow_1.errAsync)({
|
|
@@ -308,7 +323,11 @@ function executeStartWorkflow(input, ctx) {
|
|
|
308
323
|
const pending = (0, output_schemas_js_1.toPendingStep)(meta);
|
|
309
324
|
const preferences = v2_execution_helpers_js_1.defaultPreferences;
|
|
310
325
|
const nextIntent = (0, v2_state_conversion_js_1.deriveNextIntent)({ rehydrateOnly: false, isComplete: false, pending: meta });
|
|
311
|
-
|
|
326
|
+
const contentEnvelope = (0, step_content_envelope_js_1.buildStepContentEnvelope)({
|
|
327
|
+
meta,
|
|
328
|
+
references: resolvedReferences,
|
|
329
|
+
});
|
|
330
|
+
const parsed = output_schemas_js_1.V2StartWorkflowOutputSchema.parse({
|
|
312
331
|
continueToken: tokens.continueToken,
|
|
313
332
|
checkpointToken: tokens.checkpointToken,
|
|
314
333
|
isComplete: false,
|
|
@@ -316,7 +335,26 @@ function executeStartWorkflow(input, ctx) {
|
|
|
316
335
|
preferences,
|
|
317
336
|
nextIntent,
|
|
318
337
|
nextCall: (0, index_js_2.buildNextCall)({ continueToken: tokens.continueToken, isComplete: false, pending }),
|
|
319
|
-
})
|
|
338
|
+
});
|
|
339
|
+
return (0, neverthrow_1.okAsync)({ response: parsed, contentEnvelope });
|
|
320
340
|
});
|
|
321
341
|
});
|
|
322
342
|
}
|
|
343
|
+
function enrichPinnedSnapshotWithResolvedReferences(snapshot, references, workspacePath) {
|
|
344
|
+
if (references.length === 0) {
|
|
345
|
+
return (0, neverthrow_1.okAsync)({ snapshot, resolvedReferences: [] });
|
|
346
|
+
}
|
|
347
|
+
return neverthrow_1.ResultAsync.fromPromise((0, v2_reference_resolver_js_1.resolveWorkflowReferences)(references, workspacePath), () => ({ kind: 'reference_resolution_failed' })).map((result) => {
|
|
348
|
+
for (const warning of result.warnings) {
|
|
349
|
+
console.warn(`[workrail:reference-resolution] ${warning.message}`);
|
|
350
|
+
}
|
|
351
|
+
const pinnedResolvedReferences = [...result.resolved];
|
|
352
|
+
return {
|
|
353
|
+
snapshot: {
|
|
354
|
+
...snapshot,
|
|
355
|
+
resolvedReferences: pinnedResolvedReferences,
|
|
356
|
+
},
|
|
357
|
+
resolvedReferences: result.resolved,
|
|
358
|
+
};
|
|
359
|
+
});
|
|
360
|
+
}
|
|
@@ -49,6 +49,8 @@ export type StartWorkflowError = {
|
|
|
49
49
|
} | {
|
|
50
50
|
readonly kind: 'prompt_render_failed';
|
|
51
51
|
readonly message: string;
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: 'reference_resolution_failed';
|
|
52
54
|
};
|
|
53
55
|
export type ContinueWorkflowError = {
|
|
54
56
|
readonly kind: 'precondition_failed';
|
|
@@ -52,6 +52,8 @@ function mapStartWorkflowErrorToToolError(e) {
|
|
|
52
52
|
return mapTokenSigningErrorToToolError(e.cause);
|
|
53
53
|
case 'prompt_render_failed':
|
|
54
54
|
return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', `WorkRail could not render the pending step prompt: ${e.message}`, { suggestion: internalSuggestion('Retry start_workflow.', 'A step referenced by the workflow was not found in the executable definition.') });
|
|
55
|
+
case 'reference_resolution_failed':
|
|
56
|
+
return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail could not resolve workflow references. This is not caused by your input.', { suggestion: internalSuggestion('Retry start_workflow.', 'Reference resolution encountered an unexpected error.') });
|
|
55
57
|
default:
|
|
56
58
|
const _exhaustive = e;
|
|
57
59
|
return _exhaustive;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WorkflowReference } from '../../types/workflow-definition.js';
|
|
2
|
+
import type { ResolvedReference } from '../step-content-envelope.js';
|
|
3
|
+
export interface ReferenceResolutionWarning {
|
|
4
|
+
readonly referenceId: string;
|
|
5
|
+
readonly source: string;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ReferenceResolutionResult {
|
|
9
|
+
readonly resolved: readonly ResolvedReference[];
|
|
10
|
+
readonly warnings: readonly ReferenceResolutionWarning[];
|
|
11
|
+
}
|
|
12
|
+
export type FileExistsPort = (filePath: string) => Promise<boolean>;
|
|
13
|
+
export declare const defaultFileExists: FileExistsPort;
|
|
14
|
+
export declare function resolveWorkflowReferences(references: readonly WorkflowReference[], workspacePath: string, fileExists?: FileExistsPort): Promise<ReferenceResolutionResult>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.defaultFileExists = void 0;
|
|
37
|
+
exports.resolveWorkflowReferences = resolveWorkflowReferences;
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
40
|
+
const defaultFileExists = async (filePath) => {
|
|
41
|
+
try {
|
|
42
|
+
const stat = await fs.stat(filePath);
|
|
43
|
+
return stat.isFile();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.defaultFileExists = defaultFileExists;
|
|
50
|
+
function getPackageRoot() {
|
|
51
|
+
const thisDir = __dirname;
|
|
52
|
+
return path.resolve(thisDir, '..', '..', '..');
|
|
53
|
+
}
|
|
54
|
+
async function resolveWorkflowReferences(references, workspacePath, fileExists = exports.defaultFileExists) {
|
|
55
|
+
const normalizedWorkspaceBase = path.resolve(workspacePath) + path.sep;
|
|
56
|
+
const packageRoot = getPackageRoot();
|
|
57
|
+
const normalizedPackageBase = path.resolve(packageRoot) + path.sep;
|
|
58
|
+
const entries = await Promise.all(references.map(async (ref) => {
|
|
59
|
+
const isPackageRef = ref.resolveFrom === 'package';
|
|
60
|
+
const resolveBase = isPackageRef ? packageRoot : workspacePath;
|
|
61
|
+
const normalizedBase = isPackageRef ? normalizedPackageBase : normalizedWorkspaceBase;
|
|
62
|
+
const resolvedPath = path.resolve(resolveBase, ref.source);
|
|
63
|
+
if (!resolvedPath.startsWith(normalizedBase) && resolvedPath !== path.resolve(resolveBase)) {
|
|
64
|
+
return { ref, resolvedPath, exists: false, escaped: true, ioError: null };
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const exists = await fileExists(resolvedPath);
|
|
68
|
+
return { ref, resolvedPath, exists, escaped: false, ioError: null };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
+
return { ref, resolvedPath, exists: false, escaped: false, ioError: message };
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
const resolved = [];
|
|
76
|
+
const warnings = [];
|
|
77
|
+
for (const { ref, resolvedPath, exists, escaped, ioError } of entries) {
|
|
78
|
+
if (escaped) {
|
|
79
|
+
warnings.push({
|
|
80
|
+
referenceId: ref.id,
|
|
81
|
+
source: ref.source,
|
|
82
|
+
message: `Reference '${ref.id}' source path escapes ${ref.resolveFrom === 'package' ? 'package' : 'workspace'} boundary: ${ref.source}`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else if (ioError != null) {
|
|
86
|
+
warnings.push({
|
|
87
|
+
referenceId: ref.id,
|
|
88
|
+
source: ref.source,
|
|
89
|
+
message: `Reference '${ref.id}' source path could not be checked: ${ioError}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else if (!exists) {
|
|
93
|
+
warnings.push({
|
|
94
|
+
referenceId: ref.id,
|
|
95
|
+
source: ref.source,
|
|
96
|
+
message: `Reference '${ref.id}' source path does not exist: ${resolvedPath}`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const base = {
|
|
100
|
+
id: ref.id,
|
|
101
|
+
title: ref.title,
|
|
102
|
+
source: ref.source,
|
|
103
|
+
purpose: ref.purpose,
|
|
104
|
+
authoritative: ref.authoritative,
|
|
105
|
+
resolveFrom: (ref.resolveFrom ?? 'workspace'),
|
|
106
|
+
};
|
|
107
|
+
resolved.push((!escaped && exists)
|
|
108
|
+
? { ...base, status: 'resolved', resolvedPath }
|
|
109
|
+
: { ...base, status: 'unresolved' });
|
|
110
|
+
}
|
|
111
|
+
return { resolved, warnings };
|
|
112
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ResultAsync as RA } from 'neverthrow';
|
|
2
|
+
import type { WorkflowReference } from '../../types/workflow-definition.js';
|
|
3
|
+
import type { StepMetadata } from '../../v2/durable-core/domain/prompt-renderer.js';
|
|
4
|
+
import { type StepContentEnvelope } from '../step-content-envelope.js';
|
|
5
|
+
export declare function resolveRefsAndBuildEnvelope<E>(meta: StepMetadata, workflowRefs: readonly WorkflowReference[], workspacePath: string, toError: () => E): RA<StepContentEnvelope, E>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRefsAndBuildEnvelope = resolveRefsAndBuildEnvelope;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const v2_reference_resolver_js_1 = require("./v2-reference-resolver.js");
|
|
6
|
+
const step_content_envelope_js_1 = require("../step-content-envelope.js");
|
|
7
|
+
function resolveRefsAndBuildEnvelope(meta, workflowRefs, workspacePath, toError) {
|
|
8
|
+
return neverthrow_1.ResultAsync.fromPromise((0, v2_reference_resolver_js_1.resolveWorkflowReferences)(workflowRefs, workspacePath), toError).andThen((refResult) => {
|
|
9
|
+
for (const warning of refResult.warnings) {
|
|
10
|
+
console.warn(`[workrail:reference-resolution] ${warning.message}`);
|
|
11
|
+
}
|
|
12
|
+
return (0, neverthrow_1.okAsync)((0, step_content_envelope_js_1.buildStepContentEnvelope)({
|
|
13
|
+
meta,
|
|
14
|
+
references: refResult.resolved,
|
|
15
|
+
}));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -127,11 +127,13 @@ async function handleV2InspectWorkflow(input, ctx) {
|
|
|
127
127
|
const body = input.mode === 'metadata'
|
|
128
128
|
? { schemaVersion: compiled.schemaVersion, sourceKind: compiled.sourceKind, workflowId: compiled.workflowId }
|
|
129
129
|
: compiled;
|
|
130
|
+
const references = workflow.definition.references;
|
|
130
131
|
const payload = output_schemas_js_1.V2WorkflowInspectOutputSchema.parse({
|
|
131
132
|
workflowId: input.workflowId,
|
|
132
133
|
workflowHash,
|
|
133
134
|
mode: input.mode,
|
|
134
135
|
compiled: body,
|
|
136
|
+
...(references != null && references.length > 0 ? { references } : {}),
|
|
135
137
|
});
|
|
136
138
|
return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
|
|
137
139
|
});
|
|
@@ -221,16 +221,54 @@ export declare const V2WorkflowInspectOutputSchema: z.ZodObject<{
|
|
|
221
221
|
workflowHash: z.ZodString;
|
|
222
222
|
mode: z.ZodEnum<["metadata", "preview"]>;
|
|
223
223
|
compiled: z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>;
|
|
224
|
+
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
225
|
+
id: z.ZodString;
|
|
226
|
+
title: z.ZodString;
|
|
227
|
+
source: z.ZodString;
|
|
228
|
+
purpose: z.ZodString;
|
|
229
|
+
authoritative: z.ZodBoolean;
|
|
230
|
+
resolveFrom: z.ZodOptional<z.ZodEnum<["workspace", "package"]>>;
|
|
231
|
+
}, "strip", z.ZodTypeAny, {
|
|
232
|
+
id: string;
|
|
233
|
+
title: string;
|
|
234
|
+
source: string;
|
|
235
|
+
purpose: string;
|
|
236
|
+
authoritative: boolean;
|
|
237
|
+
resolveFrom?: "workspace" | "package" | undefined;
|
|
238
|
+
}, {
|
|
239
|
+
id: string;
|
|
240
|
+
title: string;
|
|
241
|
+
source: string;
|
|
242
|
+
purpose: string;
|
|
243
|
+
authoritative: boolean;
|
|
244
|
+
resolveFrom?: "workspace" | "package" | undefined;
|
|
245
|
+
}>, "many">>;
|
|
224
246
|
}, "strip", z.ZodTypeAny, {
|
|
225
247
|
workflowId: string;
|
|
226
248
|
workflowHash: string;
|
|
227
249
|
mode: "metadata" | "preview";
|
|
228
250
|
compiled: JsonValue;
|
|
251
|
+
references?: {
|
|
252
|
+
id: string;
|
|
253
|
+
title: string;
|
|
254
|
+
source: string;
|
|
255
|
+
purpose: string;
|
|
256
|
+
authoritative: boolean;
|
|
257
|
+
resolveFrom?: "workspace" | "package" | undefined;
|
|
258
|
+
}[] | undefined;
|
|
229
259
|
}, {
|
|
230
260
|
workflowId: string;
|
|
231
261
|
workflowHash: string;
|
|
232
262
|
mode: "metadata" | "preview";
|
|
233
263
|
compiled: JsonValue;
|
|
264
|
+
references?: {
|
|
265
|
+
id: string;
|
|
266
|
+
title: string;
|
|
267
|
+
source: string;
|
|
268
|
+
purpose: string;
|
|
269
|
+
authoritative: boolean;
|
|
270
|
+
resolveFrom?: "workspace" | "package" | undefined;
|
|
271
|
+
}[] | undefined;
|
|
234
272
|
}>;
|
|
235
273
|
export declare const V2PendingStepSchema: z.ZodObject<{
|
|
236
274
|
stepId: z.ZodString;
|
|
@@ -63,6 +63,14 @@ exports.V2WorkflowInspectOutputSchema = zod_1.z.object({
|
|
|
63
63
|
workflowHash: zod_1.z.string().min(1),
|
|
64
64
|
mode: zod_1.z.enum(['metadata', 'preview']),
|
|
65
65
|
compiled: exports.JsonValueSchema,
|
|
66
|
+
references: zod_1.z.array(zod_1.z.object({
|
|
67
|
+
id: zod_1.z.string().min(1),
|
|
68
|
+
title: zod_1.z.string().min(1),
|
|
69
|
+
source: zod_1.z.string().min(1),
|
|
70
|
+
purpose: zod_1.z.string().min(1),
|
|
71
|
+
authoritative: zod_1.z.boolean(),
|
|
72
|
+
resolveFrom: zod_1.z.enum(['workspace', 'package']).optional(),
|
|
73
|
+
})).optional(),
|
|
66
74
|
});
|
|
67
75
|
exports.V2PendingStepSchema = zod_1.z.object({
|
|
68
76
|
stepId: zod_1.z.string().min(1),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StepContentEnvelope } from './step-content-envelope.js';
|
|
2
|
+
export declare const V2_EXECUTION_LIFECYCLES: readonly ["start", "advance", "rehydrate"];
|
|
3
|
+
export type V2ExecutionResponseLifecycle = (typeof V2_EXECUTION_LIFECYCLES)[number];
|
|
4
|
+
export interface V2ExecutionRenderEnvelope<TResponse> {
|
|
5
|
+
readonly kind: 'v2_execution_render_envelope';
|
|
6
|
+
readonly response: TResponse;
|
|
7
|
+
readonly lifecycle: V2ExecutionResponseLifecycle;
|
|
8
|
+
readonly contentEnvelope?: StepContentEnvelope;
|
|
9
|
+
}
|
|
10
|
+
export declare function createV2ExecutionRenderEnvelope<TResponse>(args: {
|
|
11
|
+
readonly response: TResponse;
|
|
12
|
+
readonly lifecycle: V2ExecutionResponseLifecycle;
|
|
13
|
+
readonly contentEnvelope?: StepContentEnvelope;
|
|
14
|
+
}): V2ExecutionRenderEnvelope<TResponse>;
|
|
15
|
+
export declare function attachV2ExecutionRenderMetadata<TResponse extends object>(args: {
|
|
16
|
+
readonly response: TResponse;
|
|
17
|
+
readonly lifecycle: V2ExecutionResponseLifecycle;
|
|
18
|
+
readonly contentEnvelope?: StepContentEnvelope;
|
|
19
|
+
}): TResponse;
|
|
20
|
+
export declare function getV2ExecutionRenderEnvelope(value: unknown): V2ExecutionRenderEnvelope<unknown> | null;
|
|
21
|
+
export declare function isV2ExecutionRenderEnvelope(value: unknown): value is V2ExecutionRenderEnvelope<unknown>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.V2_EXECUTION_LIFECYCLES = void 0;
|
|
4
|
+
exports.createV2ExecutionRenderEnvelope = createV2ExecutionRenderEnvelope;
|
|
5
|
+
exports.attachV2ExecutionRenderMetadata = attachV2ExecutionRenderMetadata;
|
|
6
|
+
exports.getV2ExecutionRenderEnvelope = getV2ExecutionRenderEnvelope;
|
|
7
|
+
exports.isV2ExecutionRenderEnvelope = isV2ExecutionRenderEnvelope;
|
|
8
|
+
exports.V2_EXECUTION_LIFECYCLES = ['start', 'advance', 'rehydrate'];
|
|
9
|
+
const V2_EXECUTION_RENDER_META = Symbol.for('workrail.v2ExecutionRenderMeta');
|
|
10
|
+
function createV2ExecutionRenderEnvelope(args) {
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
kind: 'v2_execution_render_envelope',
|
|
13
|
+
response: args.response,
|
|
14
|
+
lifecycle: args.lifecycle,
|
|
15
|
+
...(args.contentEnvelope != null ? { contentEnvelope: args.contentEnvelope } : {}),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const VALID_LIFECYCLES = new Set(exports.V2_EXECUTION_LIFECYCLES);
|
|
19
|
+
function attachV2ExecutionRenderMetadata(args) {
|
|
20
|
+
Object.defineProperty(args.response, V2_EXECUTION_RENDER_META, {
|
|
21
|
+
value: Object.freeze({
|
|
22
|
+
lifecycle: args.lifecycle,
|
|
23
|
+
...(args.contentEnvelope != null ? { contentEnvelope: args.contentEnvelope } : {}),
|
|
24
|
+
}),
|
|
25
|
+
enumerable: false,
|
|
26
|
+
configurable: false,
|
|
27
|
+
writable: false,
|
|
28
|
+
});
|
|
29
|
+
return args.response;
|
|
30
|
+
}
|
|
31
|
+
function getV2ExecutionRenderEnvelope(value) {
|
|
32
|
+
if (typeof value !== 'object' || value === null)
|
|
33
|
+
return null;
|
|
34
|
+
const candidate = value;
|
|
35
|
+
if (candidate.kind === 'v2_execution_render_envelope' &&
|
|
36
|
+
'response' in candidate &&
|
|
37
|
+
typeof candidate.lifecycle === 'string' &&
|
|
38
|
+
VALID_LIFECYCLES.has(candidate.lifecycle)) {
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
const metadata = Reflect.get(candidate, V2_EXECUTION_RENDER_META);
|
|
42
|
+
if (metadata == null || !VALID_LIFECYCLES.has(metadata.lifecycle)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return createV2ExecutionRenderEnvelope({
|
|
46
|
+
response: value,
|
|
47
|
+
lifecycle: metadata.lifecycle,
|
|
48
|
+
contentEnvelope: metadata.contentEnvelope,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function isV2ExecutionRenderEnvelope(value) {
|
|
52
|
+
if (typeof value !== 'object' || value === null)
|
|
53
|
+
return false;
|
|
54
|
+
const candidate = value;
|
|
55
|
+
return (candidate.kind === 'v2_execution_render_envelope' &&
|
|
56
|
+
'response' in candidate &&
|
|
57
|
+
typeof candidate.lifecycle === 'string' &&
|
|
58
|
+
VALID_LIFECYCLES.has(candidate.lifecycle));
|
|
59
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { V2ExecutionResponseLifecycle } from './render-envelope.js';
|
|
2
|
+
export type SupplementKind = 'authority_context' | 'notes_guidance';
|
|
3
|
+
export interface FormattedSupplement {
|
|
4
|
+
readonly kind: SupplementKind;
|
|
5
|
+
readonly order: number;
|
|
6
|
+
readonly text: string;
|
|
7
|
+
}
|
|
8
|
+
export type SupplementDelivery = {
|
|
9
|
+
readonly mode: 'per_lifecycle';
|
|
10
|
+
} | {
|
|
11
|
+
readonly mode: 'once_per_session';
|
|
12
|
+
readonly emitOn: V2ExecutionResponseLifecycle;
|
|
13
|
+
};
|
|
14
|
+
export declare function buildResponseSupplements(args: {
|
|
15
|
+
readonly lifecycle: V2ExecutionResponseLifecycle;
|
|
16
|
+
readonly cleanFormat: boolean;
|
|
17
|
+
}): readonly FormattedSupplement[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildResponseSupplements = buildResponseSupplements;
|
|
4
|
+
const AUTHORITY_CONTEXT = [
|
|
5
|
+
'WorkRail is a separate live system the user is actively using to direct this task.',
|
|
6
|
+
'Treat the main content item from WorkRail as the instruction to follow now.',
|
|
7
|
+
].join('\n');
|
|
8
|
+
const NOTES_GUIDANCE = [
|
|
9
|
+
'How to write good notes (output.notesMarkdown):',
|
|
10
|
+
'- Write for a human reader reviewing your work later.',
|
|
11
|
+
'- Include: what you did and key decisions, what you produced (files, functions, test results, specific numbers), anything notable (risks, open questions, things you deliberately chose NOT to do and why).',
|
|
12
|
+
'- Use markdown: headings, bullets, bold, code refs. Be specific — file paths, function names, counts.',
|
|
13
|
+
'- Scope: THIS step only. WorkRail concatenates notes across steps automatically.',
|
|
14
|
+
'- 10-30 lines is ideal. Too short is worse than too long.',
|
|
15
|
+
'- Omitting notes will block the step.',
|
|
16
|
+
].join('\n');
|
|
17
|
+
function defineResponseSupplement(spec) {
|
|
18
|
+
if (spec.delivery.mode === 'once_per_session' &&
|
|
19
|
+
!spec.lifecycles.includes(spec.delivery.emitOn)) {
|
|
20
|
+
throw new Error(`Supplement "${spec.kind}" has once_per_session delivery on "${spec.delivery.emitOn}" but that lifecycle is not enabled.`);
|
|
21
|
+
}
|
|
22
|
+
return spec;
|
|
23
|
+
}
|
|
24
|
+
function shouldEmitSupplement(spec, lifecycle) {
|
|
25
|
+
if (!spec.lifecycles.includes(lifecycle))
|
|
26
|
+
return false;
|
|
27
|
+
if (spec.delivery.mode === 'per_lifecycle')
|
|
28
|
+
return true;
|
|
29
|
+
return spec.delivery.emitOn === lifecycle;
|
|
30
|
+
}
|
|
31
|
+
const CLEAN_RESPONSE_SUPPLEMENTS = [
|
|
32
|
+
defineResponseSupplement({
|
|
33
|
+
kind: 'authority_context',
|
|
34
|
+
order: 10,
|
|
35
|
+
lifecycles: ['start', 'rehydrate'],
|
|
36
|
+
delivery: { mode: 'per_lifecycle' },
|
|
37
|
+
renderText: () => AUTHORITY_CONTEXT,
|
|
38
|
+
}),
|
|
39
|
+
defineResponseSupplement({
|
|
40
|
+
kind: 'notes_guidance',
|
|
41
|
+
order: 20,
|
|
42
|
+
lifecycles: ['start', 'rehydrate'],
|
|
43
|
+
delivery: { mode: 'once_per_session', emitOn: 'start' },
|
|
44
|
+
renderText: () => NOTES_GUIDANCE,
|
|
45
|
+
}),
|
|
46
|
+
];
|
|
47
|
+
function buildResponseSupplements(args) {
|
|
48
|
+
if (!args.cleanFormat)
|
|
49
|
+
return [];
|
|
50
|
+
return CLEAN_RESPONSE_SUPPLEMENTS
|
|
51
|
+
.filter((spec) => shouldEmitSupplement(spec, args.lifecycle))
|
|
52
|
+
.map((spec) => ({
|
|
53
|
+
kind: spec.kind,
|
|
54
|
+
order: spec.order,
|
|
55
|
+
text: spec.renderText(),
|
|
56
|
+
}))
|
|
57
|
+
.sort((left, right) => left.order - right.order);
|
|
58
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { StepMetadata } from '../v2/durable-core/domain/prompt-renderer.js';
|
|
2
|
+
import type { FormattedSupplement } from './response-supplements.js';
|
|
3
|
+
interface ResolvedReferenceBase {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly source: string;
|
|
7
|
+
readonly purpose: string;
|
|
8
|
+
readonly authoritative: boolean;
|
|
9
|
+
readonly resolveFrom: 'workspace' | 'package';
|
|
10
|
+
}
|
|
11
|
+
export type ResolvedReference = (ResolvedReferenceBase & {
|
|
12
|
+
readonly status: 'resolved';
|
|
13
|
+
readonly resolvedPath: string;
|
|
14
|
+
}) | (ResolvedReferenceBase & {
|
|
15
|
+
readonly status: 'unresolved';
|
|
16
|
+
}) | (ResolvedReferenceBase & {
|
|
17
|
+
readonly status: 'pinned';
|
|
18
|
+
});
|
|
19
|
+
export interface StepContentEnvelope {
|
|
20
|
+
readonly stepId: string;
|
|
21
|
+
readonly title: string;
|
|
22
|
+
readonly authoredPrompt: string;
|
|
23
|
+
readonly agentRole?: string;
|
|
24
|
+
readonly references: readonly ResolvedReference[];
|
|
25
|
+
readonly supplements: readonly FormattedSupplement[];
|
|
26
|
+
}
|
|
27
|
+
export declare function buildStepContentEnvelope(args: {
|
|
28
|
+
readonly meta: StepMetadata;
|
|
29
|
+
readonly references?: readonly ResolvedReference[];
|
|
30
|
+
readonly supplements?: readonly FormattedSupplement[];
|
|
31
|
+
}): StepContentEnvelope;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildStepContentEnvelope = buildStepContentEnvelope;
|
|
4
|
+
function buildStepContentEnvelope(args) {
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
stepId: args.meta.stepId,
|
|
7
|
+
title: args.meta.title,
|
|
8
|
+
authoredPrompt: args.meta.prompt,
|
|
9
|
+
agentRole: args.meta.agentRole,
|
|
10
|
+
references: Object.freeze(args.references ?? []),
|
|
11
|
+
supplements: Object.freeze(args.supplements ?? []),
|
|
12
|
+
});
|
|
13
|
+
}
|