@exaudeus/workrail 1.16.0 → 1.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.
- package/dist/application/services/compiler/feature-registry.js +18 -0
- package/dist/application/services/compiler/prompt-blocks.js +2 -1
- package/dist/application/services/compiler/ref-registry.js +36 -0
- package/dist/application/services/validation-engine.d.ts +1 -0
- package/dist/application/services/validation-engine.js +14 -0
- package/dist/application/services/workflow-validation-pipeline.d.ts +96 -0
- package/dist/application/services/workflow-validation-pipeline.js +94 -0
- package/dist/application/use-cases/raw-workflow-file-scanner.d.ts +18 -0
- package/dist/application/use-cases/raw-workflow-file-scanner.js +91 -0
- package/dist/application/use-cases/validate-workflow-file.d.ts +17 -0
- package/dist/application/use-cases/validate-workflow-file.js +96 -0
- package/dist/application/use-cases/validate-workflow-json.d.ts +2 -1
- package/dist/application/use-cases/validate-workflow-json.js +67 -13
- package/dist/application/use-cases/validate-workflow-registry.d.ts +72 -0
- package/dist/application/use-cases/validate-workflow-registry.js +215 -0
- package/dist/application/validation.d.ts +4 -0
- package/dist/application/validation.js +16 -0
- package/dist/cli/commands/validate.js +15 -0
- package/dist/cli.js +10 -1
- package/dist/infrastructure/storage/caching-workflow-storage.d.ts +1 -0
- package/dist/infrastructure/storage/caching-workflow-storage.js +3 -0
- package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.d.ts +2 -1
- package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.js +8 -21
- package/dist/infrastructure/storage/file-workflow-storage.d.ts +0 -1
- package/dist/infrastructure/storage/file-workflow-storage.js +15 -36
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +1 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +16 -6
- package/dist/infrastructure/storage/workflow-resolution.d.ts +62 -0
- package/dist/infrastructure/storage/workflow-resolution.js +150 -0
- package/dist/manifest.json +140 -68
- package/dist/mcp/handlers/v2-execution/replay.d.ts +1 -1
- package/dist/mcp/handlers/v2-execution/replay.js +37 -21
- package/dist/mcp/handlers/v2-execution/start.js +35 -13
- package/dist/mcp/handlers/v2-execution-helpers.d.ts +9 -11
- package/dist/mcp/handlers/v2-execution-helpers.js +6 -18
- package/dist/mcp/output-schemas.d.ts +20 -20
- package/dist/mcp/server.d.ts +18 -0
- package/dist/mcp/server.js +8 -1
- package/dist/mcp/transports/http-entry.d.ts +1 -0
- package/dist/mcp/transports/http-entry.js +87 -0
- package/dist/mcp/transports/http-listener.d.ts +9 -0
- package/dist/mcp/transports/http-listener.js +64 -0
- package/dist/mcp/transports/stdio-entry.d.ts +1 -0
- package/dist/mcp/transports/stdio-entry.js +92 -0
- package/dist/mcp/transports/transport-mode.d.ts +7 -0
- package/dist/mcp/transports/transport-mode.js +18 -0
- package/dist/mcp-server.js +21 -5
- package/dist/types/storage.d.ts +1 -0
- package/dist/v2/durable-core/domain/prompt-renderer.js +13 -7
- package/dist/v2/durable-core/domain/start-construction.d.ts +22 -0
- package/dist/v2/durable-core/domain/start-construction.js +31 -0
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +8 -8
- package/dist/v2/read-only/v1-to-v2-shim.d.ts +5 -0
- package/dist/v2/read-only/v1-to-v2-shim.js +18 -0
- package/package.json +3 -2
- package/workflows/bug-investigation.agentic.v2.json +134 -0
- package/workflows/mr-review-workflow.agentic.v2.json +238 -0
|
@@ -11,6 +11,24 @@ const FEATURE_DEFINITIONS = [
|
|
|
11
11
|
],
|
|
12
12
|
],
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
id: 'wr.features.subagent_guidance',
|
|
16
|
+
constraints: [
|
|
17
|
+
'Use the WorkRail Executor as the only subagent model. Do not refer to or rely on named Builder, Researcher, or other identities.',
|
|
18
|
+
'The main agent owns strategy, decisions, synthesis, and final outputs. Subagents provide independent cognitive perspectives only.',
|
|
19
|
+
[
|
|
20
|
+
{ kind: 'ref', refId: 'wr.refs.parallelize_cognition_serialize_synthesis' },
|
|
21
|
+
],
|
|
22
|
+
],
|
|
23
|
+
procedure: [
|
|
24
|
+
'When delegating to WorkRail Executors, provide each with a clear non-overlapping focus and the shared fact packet or context as primary truth.',
|
|
25
|
+
'After receiving parallel subagent outputs, synthesize them yourself — do not let any single subagent output become the canonical answer without main-agent review.',
|
|
26
|
+
],
|
|
27
|
+
verify: [
|
|
28
|
+
'All delegated outputs were synthesized by the main agent, not adopted verbatim.',
|
|
29
|
+
'No subagent was given final decision authority over canonical findings, recommendations, or artifacts.',
|
|
30
|
+
],
|
|
31
|
+
},
|
|
14
32
|
];
|
|
15
33
|
function createFeatureRegistry() {
|
|
16
34
|
const byId = new Map(FEATURE_DEFINITIONS.map(f => [f.id, f]));
|
|
@@ -22,6 +22,42 @@ const REF_CONTENT = {
|
|
|
22
22
|
'- Use workspace-scoped queries to find related past work',
|
|
23
23
|
'- Do not block on Memory failures — proceed without prior context if unavailable',
|
|
24
24
|
].join('\n'),
|
|
25
|
+
'wr.refs.notes_first_durability': [
|
|
26
|
+
'Durability rules (notes-first):',
|
|
27
|
+
'- Use output.notesMarkdown as the primary durable record for every step',
|
|
28
|
+
'- Keep execution truth in notes and explicit context variables, not in markdown sidecar files',
|
|
29
|
+
'- Human-facing artifacts (review docs, plans) are for readability only — they are NOT required workflow memory',
|
|
30
|
+
'- If a chat rewind occurs, the durable notes and context variables survive; sidecar files may not',
|
|
31
|
+
'- Always record key decisions, findings, and rationale in notesMarkdown before advancing',
|
|
32
|
+
].join('\n'),
|
|
33
|
+
'wr.refs.synthesis_under_disagreement': [
|
|
34
|
+
'Synthesis rules when parallel outputs disagree:',
|
|
35
|
+
'- Treat disagreement as first-class work — never handwave contradictory outputs',
|
|
36
|
+
'- If 2+ parallel outputs flag the same issue at the same severity, treat it as validated',
|
|
37
|
+
'- If the same issue is flagged at different severities, default to the higher severity unless the lower-severity position includes specific counter-evidence',
|
|
38
|
+
'- If one output flags an issue and others are silent, investigate it but do not automatically block unless it is clearly critical',
|
|
39
|
+
'- If one output says false positive and another says valid issue, require explicit main-agent adjudication in notes before finalization',
|
|
40
|
+
'- If outputs show material disagreement, findings override any preliminary recommendation until the disagreement is reconciled',
|
|
41
|
+
'- Document every resolved contradiction with its resolution rationale',
|
|
42
|
+
].join('\n'),
|
|
43
|
+
'wr.refs.parallelize_cognition_serialize_synthesis': [
|
|
44
|
+
'Parallelism rules:',
|
|
45
|
+
'- Parallelize independent cognition: context gathering, hypothesis generation, audit routines, and reviewer families can run simultaneously',
|
|
46
|
+
'- Serialize synthesis: merging parallel outputs, resolving contradictions, making canonical decisions, and writing final artifacts must happen sequentially by the main agent',
|
|
47
|
+
'- Never let a parallel subagent finalize or commit to a canonical answer — the main agent owns synthesis and final decisions',
|
|
48
|
+
'- Prefer one compact targeted bundle over multiple small sequential delegation moments',
|
|
49
|
+
'- When spawning parallel executors, give each a clear, non-overlapping focus area',
|
|
50
|
+
].join('\n'),
|
|
51
|
+
'wr.refs.adversarial_challenge_rules': [
|
|
52
|
+
'Adversarial challenge rules:',
|
|
53
|
+
'- Actively try to break the current leading position — do not just look for confirming evidence',
|
|
54
|
+
'- For each finding or conclusion, ask: what evidence would disprove this? Is that evidence missing or present?',
|
|
55
|
+
'- If the challenge cannot materially weaken the position, the position is strengthened',
|
|
56
|
+
'- If the challenge reveals a genuine weakness, it must be recorded as a finding and the position must be updated',
|
|
57
|
+
'- Do not soften findings to avoid conflict — severity should reflect actual risk, not social comfort',
|
|
58
|
+
'- Challenge severity inflation as well as severity deflation — false positives waste as much attention as false negatives',
|
|
59
|
+
'- If 2+ independent challengers raise the same serious concern, treat it as blocking by default',
|
|
60
|
+
].join('\n'),
|
|
25
61
|
};
|
|
26
62
|
function createRefRegistry() {
|
|
27
63
|
const knownIds = Object.keys(REF_CONTENT);
|
|
@@ -40,6 +40,7 @@ export declare class ValidationEngine {
|
|
|
40
40
|
private formatDefaultContainsMessage;
|
|
41
41
|
validateLoopStep(step: LoopStepDefinition, workflow: Workflow): ValidationResult;
|
|
42
42
|
validateWorkflow(workflow: Workflow): ValidationResult;
|
|
43
|
+
validateWorkflowStructureOnly(workflow: Workflow): Result<Workflow, readonly string[]>;
|
|
43
44
|
private collectQuotedJsonValidationMessageWarnings;
|
|
44
45
|
private collectValidationRuleMessages;
|
|
45
46
|
private isValidVariableName;
|
|
@@ -490,6 +490,13 @@ let ValidationEngine = ValidationEngine_1 = class ValidationEngine {
|
|
|
490
490
|
issues.push(`Step '${step.id}' must have prompt, promptBlocks, or templateCall`);
|
|
491
491
|
suggestions.push('Add a prompt string, structured promptBlocks, or a templateCall to each step');
|
|
492
492
|
}
|
|
493
|
+
const typedStep = step;
|
|
494
|
+
const promptSourceCount = (typedStep.prompt ? 1 : 0) +
|
|
495
|
+
(typedStep.promptBlocks ? 1 : 0) +
|
|
496
|
+
(typedStep.templateCall ? 1 : 0);
|
|
497
|
+
if (promptSourceCount > 1) {
|
|
498
|
+
issues.push(`Step '${step.id}' declares multiple prompt sources (prompt, promptBlocks, templateCall) — use exactly one`);
|
|
499
|
+
}
|
|
493
500
|
this.collectQuotedJsonValidationMessageWarnings(step, `Step '${step.id}'`, warnings);
|
|
494
501
|
const callValidation = this.validateStepFunctionCalls(step, workflow.definition.functionDefinitions || []);
|
|
495
502
|
if (!callValidation.valid) {
|
|
@@ -527,6 +534,13 @@ let ValidationEngine = ValidationEngine_1 = class ValidationEngine {
|
|
|
527
534
|
info: info.length > 0 ? info : undefined
|
|
528
535
|
};
|
|
529
536
|
}
|
|
537
|
+
validateWorkflowStructureOnly(workflow) {
|
|
538
|
+
const result = this.validateWorkflow(workflow);
|
|
539
|
+
if (result.valid) {
|
|
540
|
+
return (0, neverthrow_1.ok)(workflow);
|
|
541
|
+
}
|
|
542
|
+
return (0, neverthrow_1.err)(result.issues);
|
|
543
|
+
}
|
|
530
544
|
collectQuotedJsonValidationMessageWarnings(stepLike, prefix, warnings) {
|
|
531
545
|
const criteria = stepLike?.validationCriteria;
|
|
532
546
|
if (!criteria)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Workflow } from '../../types/workflow.js';
|
|
2
|
+
import type { DomainError } from '../../domain/execution/error.js';
|
|
3
|
+
import type { WorkflowCompiler, CompiledWorkflow } from './workflow-compiler.js';
|
|
4
|
+
import type { WorkflowInterpreter } from './workflow-interpreter.js';
|
|
5
|
+
import { type Result } from 'neverthrow';
|
|
6
|
+
import type { CompiledWorkflowSnapshotV1 } from '../../v2/durable-core/schemas/compiled-workflow/index.js';
|
|
7
|
+
import type { StartabilityFailure } from '../../v2/durable-core/domain/start-construction.js';
|
|
8
|
+
export interface SchemaError {
|
|
9
|
+
readonly instancePath: string;
|
|
10
|
+
readonly message?: string;
|
|
11
|
+
readonly keyword?: string;
|
|
12
|
+
readonly params?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export type ExecutableCompiledWorkflowSnapshot = Extract<CompiledWorkflowSnapshotV1, {
|
|
15
|
+
sourceKind: 'v1_pinned';
|
|
16
|
+
}>;
|
|
17
|
+
export type ValidationOutcomePhase1a = {
|
|
18
|
+
readonly kind: 'schema_failed';
|
|
19
|
+
readonly workflowId: string;
|
|
20
|
+
readonly errors: readonly SchemaError[];
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'structural_failed';
|
|
23
|
+
readonly workflowId: string;
|
|
24
|
+
readonly issues: readonly string[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'v1_compilation_failed';
|
|
27
|
+
readonly workflowId: string;
|
|
28
|
+
readonly cause: DomainError;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'normalization_failed';
|
|
31
|
+
readonly workflowId: string;
|
|
32
|
+
readonly cause: DomainError;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'executable_compilation_failed';
|
|
35
|
+
readonly workflowId: string;
|
|
36
|
+
readonly cause: DomainError;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: 'phase1a_valid';
|
|
39
|
+
readonly workflowId: string;
|
|
40
|
+
readonly snapshot: ExecutableCompiledWorkflowSnapshot;
|
|
41
|
+
};
|
|
42
|
+
export type ValidationOutcome = {
|
|
43
|
+
readonly kind: 'schema_failed';
|
|
44
|
+
readonly workflowId: string;
|
|
45
|
+
readonly errors: readonly SchemaError[];
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: 'structural_failed';
|
|
48
|
+
readonly workflowId: string;
|
|
49
|
+
readonly issues: readonly string[];
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'v1_compilation_failed';
|
|
52
|
+
readonly workflowId: string;
|
|
53
|
+
readonly cause: DomainError;
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: 'normalization_failed';
|
|
56
|
+
readonly workflowId: string;
|
|
57
|
+
readonly cause: DomainError;
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: 'round_trip_failed';
|
|
60
|
+
readonly workflowId: string;
|
|
61
|
+
readonly cause: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: 'v2_compilation_failed';
|
|
64
|
+
readonly workflowId: string;
|
|
65
|
+
readonly cause: DomainError;
|
|
66
|
+
} | {
|
|
67
|
+
readonly kind: 'startability_failed';
|
|
68
|
+
readonly workflowId: string;
|
|
69
|
+
readonly reason: StartabilityFailure;
|
|
70
|
+
} | {
|
|
71
|
+
readonly kind: 'valid';
|
|
72
|
+
readonly validated: ValidatedWorkflow;
|
|
73
|
+
};
|
|
74
|
+
export interface ValidatedWorkflow {
|
|
75
|
+
readonly kind: 'validated_workflow';
|
|
76
|
+
readonly source: Workflow;
|
|
77
|
+
readonly executable: any;
|
|
78
|
+
readonly compiledV1: CompiledWorkflow;
|
|
79
|
+
readonly compiledExecutable: any;
|
|
80
|
+
}
|
|
81
|
+
export interface ValidationPipelineDepsPhase1a {
|
|
82
|
+
readonly schemaValidate: (workflow: Workflow) => Result<Workflow, readonly SchemaError[]>;
|
|
83
|
+
readonly structuralValidate: (workflow: Workflow) => Result<Workflow, readonly string[]>;
|
|
84
|
+
readonly compiler: WorkflowCompiler;
|
|
85
|
+
readonly normalizeToExecutable: (workflow: Workflow) => Result<ExecutableCompiledWorkflowSnapshot, DomainError>;
|
|
86
|
+
}
|
|
87
|
+
export interface ValidationPipelineDeps extends ValidationPipelineDepsPhase1a {
|
|
88
|
+
readonly interpreter: WorkflowInterpreter;
|
|
89
|
+
readonly resolveFirstStep: (authoredWorkflow: Workflow, pinnedSnapshot: Extract<CompiledWorkflowSnapshotV1, {
|
|
90
|
+
sourceKind: 'v1_pinned';
|
|
91
|
+
}>) => Result<{
|
|
92
|
+
readonly id: string;
|
|
93
|
+
}, StartabilityFailure>;
|
|
94
|
+
}
|
|
95
|
+
export declare function validateWorkflowPhase1a(workflow: Workflow, deps: ValidationPipelineDepsPhase1a): ValidationOutcomePhase1a;
|
|
96
|
+
export declare function validateWorkflow(workflow: Workflow, deps: ValidationPipelineDeps): ValidationOutcome;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWorkflowPhase1a = validateWorkflowPhase1a;
|
|
4
|
+
exports.validateWorkflow = validateWorkflow;
|
|
5
|
+
const workflow_js_1 = require("../../types/workflow.js");
|
|
6
|
+
const workflow_source_js_1 = require("../../types/workflow-source.js");
|
|
7
|
+
const workflow_definition_js_1 = require("../../types/workflow-definition.js");
|
|
8
|
+
const neverthrow_1 = require("neverthrow");
|
|
9
|
+
function validateWorkflowPhase1a(workflow, deps) {
|
|
10
|
+
const workflowId = workflow.definition.id;
|
|
11
|
+
const schemaResult = deps.schemaValidate(workflow);
|
|
12
|
+
if (schemaResult.isErr()) {
|
|
13
|
+
return { kind: 'schema_failed', workflowId, errors: schemaResult.error };
|
|
14
|
+
}
|
|
15
|
+
const structuralResult = deps.structuralValidate(workflow);
|
|
16
|
+
if (structuralResult.isErr()) {
|
|
17
|
+
return { kind: 'structural_failed', workflowId, issues: structuralResult.error };
|
|
18
|
+
}
|
|
19
|
+
const v1CompilationResult = deps.compiler.compile(workflow);
|
|
20
|
+
if (v1CompilationResult.isErr()) {
|
|
21
|
+
return { kind: 'v1_compilation_failed', workflowId, cause: v1CompilationResult.error };
|
|
22
|
+
}
|
|
23
|
+
const normalizationResult = deps.normalizeToExecutable(workflow);
|
|
24
|
+
if (normalizationResult.isErr()) {
|
|
25
|
+
return { kind: 'normalization_failed', workflowId, cause: normalizationResult.error };
|
|
26
|
+
}
|
|
27
|
+
const snapshot = normalizationResult.value;
|
|
28
|
+
if ((0, workflow_definition_js_1.hasWorkflowDefinitionShape)(snapshot.definition)) {
|
|
29
|
+
const executableWorkflow = (0, workflow_js_1.createWorkflow)(snapshot.definition, (0, workflow_source_js_1.createBundledSource)());
|
|
30
|
+
const execCompileResult = deps.compiler.compile(executableWorkflow);
|
|
31
|
+
if (execCompileResult.isErr()) {
|
|
32
|
+
return { kind: 'executable_compilation_failed', workflowId, cause: execCompileResult.error };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { kind: 'phase1a_valid', workflowId, snapshot };
|
|
36
|
+
}
|
|
37
|
+
function validateWorkflow(workflow, deps) {
|
|
38
|
+
const workflowId = workflow.definition.id;
|
|
39
|
+
const phase1aOutcome = validateWorkflowPhase1a(workflow, deps);
|
|
40
|
+
if (phase1aOutcome.kind !== 'phase1a_valid') {
|
|
41
|
+
return phase1aOutcome;
|
|
42
|
+
}
|
|
43
|
+
const snapshot = phase1aOutcome.snapshot;
|
|
44
|
+
let roundTrippedDefinition;
|
|
45
|
+
try {
|
|
46
|
+
const serialized = JSON.stringify(snapshot);
|
|
47
|
+
const deserialized = JSON.parse(serialized);
|
|
48
|
+
if (!deserialized?.definition) {
|
|
49
|
+
return {
|
|
50
|
+
kind: 'round_trip_failed',
|
|
51
|
+
workflowId,
|
|
52
|
+
cause: 'Definition lost during JSON round-trip',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
roundTrippedDefinition = deserialized.definition;
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return {
|
|
59
|
+
kind: 'round_trip_failed',
|
|
60
|
+
workflowId,
|
|
61
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const executableWorkflow = Object.freeze({
|
|
65
|
+
kind: 'executable_workflow',
|
|
66
|
+
definition: roundTrippedDefinition,
|
|
67
|
+
});
|
|
68
|
+
const compiledExecutable = {};
|
|
69
|
+
const startabilityResult = validateStartability(workflow, snapshot, executableWorkflow, deps);
|
|
70
|
+
if (startabilityResult.isErr()) {
|
|
71
|
+
return { kind: 'startability_failed', workflowId, reason: startabilityResult.error };
|
|
72
|
+
}
|
|
73
|
+
const v1Compiled = deps.compiler.compile(workflow).unwrapOr(undefined);
|
|
74
|
+
if (!v1Compiled) {
|
|
75
|
+
throw new Error('Invariant violation: v1 compilation failed after already passing');
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
kind: 'valid',
|
|
79
|
+
validated: {
|
|
80
|
+
kind: 'validated_workflow',
|
|
81
|
+
source: workflow,
|
|
82
|
+
executable: executableWorkflow,
|
|
83
|
+
compiledV1: v1Compiled,
|
|
84
|
+
compiledExecutable,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function validateStartability(authoredWorkflow, pinnedSnapshot, executableWorkflow, deps) {
|
|
89
|
+
const firstStepResult = deps.resolveFirstStep(authoredWorkflow, pinnedSnapshot);
|
|
90
|
+
if (firstStepResult.isErr()) {
|
|
91
|
+
return (0, neverthrow_1.err)(firstStepResult.error);
|
|
92
|
+
}
|
|
93
|
+
return (0, neverthrow_1.ok)(undefined);
|
|
94
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WorkflowDefinition } from '../../types/workflow-definition.js';
|
|
2
|
+
export declare function findWorkflowJsonFiles(baseDirReal: string): Promise<string[]>;
|
|
3
|
+
export type VariantKind = 'v2' | 'agentic' | 'standard';
|
|
4
|
+
export interface ParsedRawWorkflowFile {
|
|
5
|
+
readonly kind: 'parsed';
|
|
6
|
+
readonly filePath: string;
|
|
7
|
+
readonly relativeFilePath: string;
|
|
8
|
+
readonly definition: WorkflowDefinition;
|
|
9
|
+
readonly variantKind: VariantKind;
|
|
10
|
+
}
|
|
11
|
+
export interface UnparseableRawWorkflowFile {
|
|
12
|
+
readonly kind: 'unparseable';
|
|
13
|
+
readonly filePath: string;
|
|
14
|
+
readonly relativeFilePath: string;
|
|
15
|
+
readonly error: string;
|
|
16
|
+
}
|
|
17
|
+
export type RawWorkflowFile = ParsedRawWorkflowFile | UnparseableRawWorkflowFile;
|
|
18
|
+
export declare function scanRawWorkflowFiles(baseDirReal: string): Promise<RawWorkflowFile[]>;
|
|
@@ -0,0 +1,91 @@
|
|
|
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.findWorkflowJsonFiles = findWorkflowJsonFiles;
|
|
7
|
+
exports.scanRawWorkflowFiles = scanRawWorkflowFiles;
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
async function findWorkflowJsonFiles(baseDirReal) {
|
|
12
|
+
const files = [];
|
|
13
|
+
async function scan(currentDir) {
|
|
14
|
+
const entries = await promises_1.default.readdir(currentDir, { withFileTypes: true });
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const fullPath = path_1.default.join(currentDir, entry.name);
|
|
17
|
+
if (entry.isDirectory()) {
|
|
18
|
+
if (entry.name === 'examples') {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
await scan(fullPath);
|
|
22
|
+
}
|
|
23
|
+
else if (entry.isFile() && entry.name.endsWith('.json')) {
|
|
24
|
+
files.push(fullPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await scan(baseDirReal);
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
async function scanRawWorkflowFiles(baseDirReal) {
|
|
32
|
+
const allJsonFiles = await findWorkflowJsonFiles(baseDirReal);
|
|
33
|
+
const results = [];
|
|
34
|
+
for (const filePath of allJsonFiles) {
|
|
35
|
+
const relativeFilePath = path_1.default.relative(baseDirReal, filePath);
|
|
36
|
+
try {
|
|
37
|
+
const stats = (0, fs_1.statSync)(filePath);
|
|
38
|
+
if (stats.size > 1000000) {
|
|
39
|
+
results.push({
|
|
40
|
+
kind: 'unparseable',
|
|
41
|
+
filePath,
|
|
42
|
+
relativeFilePath,
|
|
43
|
+
error: `File exceeds size limit (1MB): ${stats.size} bytes`,
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const raw = await promises_1.default.readFile(filePath, 'utf-8');
|
|
48
|
+
const definition = JSON.parse(raw);
|
|
49
|
+
if (!isWorkflowDefinition(definition)) {
|
|
50
|
+
results.push({
|
|
51
|
+
kind: 'unparseable',
|
|
52
|
+
filePath,
|
|
53
|
+
relativeFilePath,
|
|
54
|
+
error: 'Invalid workflow definition structure (missing required fields)',
|
|
55
|
+
});
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const variantKind = detectVariantKind(relativeFilePath);
|
|
59
|
+
results.push({
|
|
60
|
+
kind: 'parsed',
|
|
61
|
+
filePath,
|
|
62
|
+
relativeFilePath,
|
|
63
|
+
definition,
|
|
64
|
+
variantKind,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
results.push({
|
|
69
|
+
kind: 'unparseable',
|
|
70
|
+
filePath,
|
|
71
|
+
relativeFilePath,
|
|
72
|
+
error: e instanceof Error ? e.message : String(e),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
function detectVariantKind(relativeFilePath) {
|
|
79
|
+
const normalized = relativeFilePath.replace(/\\/g, '/');
|
|
80
|
+
if (normalized.includes('.v2.'))
|
|
81
|
+
return 'v2';
|
|
82
|
+
if (normalized.includes('.agentic.'))
|
|
83
|
+
return 'agentic';
|
|
84
|
+
return 'standard';
|
|
85
|
+
}
|
|
86
|
+
function isWorkflowDefinition(obj) {
|
|
87
|
+
if (!obj || typeof obj !== 'object')
|
|
88
|
+
return false;
|
|
89
|
+
const def = obj;
|
|
90
|
+
return typeof def.id === 'string' && Array.isArray(def.steps);
|
|
91
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Workflow, WorkflowDefinition } from '../../types/workflow.js';
|
|
2
2
|
import type { ValidationResult } from '../../types/validation.js';
|
|
3
|
+
import { type ValidationPipelineDepsPhase1a } from '../services/workflow-validation-pipeline.js';
|
|
3
4
|
export interface SchemaValidationResult {
|
|
4
5
|
readonly valid: boolean;
|
|
5
6
|
readonly errors: readonly string[];
|
|
@@ -33,6 +34,18 @@ export type ValidateWorkflowFileResult = {
|
|
|
33
34
|
warnings?: readonly string[];
|
|
34
35
|
info?: readonly string[];
|
|
35
36
|
suggestions?: readonly string[];
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'v1_compilation_failed';
|
|
39
|
+
filePath: string;
|
|
40
|
+
message: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'normalization_failed';
|
|
43
|
+
filePath: string;
|
|
44
|
+
message: string;
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'executable_compilation_failed';
|
|
47
|
+
filePath: string;
|
|
48
|
+
message: string;
|
|
36
49
|
};
|
|
37
50
|
export interface ValidateWorkflowFileDeps {
|
|
38
51
|
readonly resolvePath: (filePath: string) => string;
|
|
@@ -43,4 +56,8 @@ export interface ValidateWorkflowFileDeps {
|
|
|
43
56
|
readonly makeRuntimeWorkflow: (definition: WorkflowDefinition, resolvedPath: string) => Workflow;
|
|
44
57
|
readonly validateRuntimeWorkflow: (workflow: Workflow) => ValidationResult;
|
|
45
58
|
}
|
|
59
|
+
export interface ValidateWorkflowFileDepsPipeline extends ValidateWorkflowFileDeps {
|
|
60
|
+
readonly validationPipelineDeps?: ValidationPipelineDepsPhase1a;
|
|
61
|
+
}
|
|
46
62
|
export declare function createValidateWorkflowFileUseCase(deps: ValidateWorkflowFileDeps): (filePath: string) => ValidateWorkflowFileResult;
|
|
63
|
+
export declare function createValidateWorkflowFileUseCasePipeline(deps: ValidateWorkflowFileDepsPipeline): (filePath: string) => ValidateWorkflowFileResult;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createValidateWorkflowFileUseCase = createValidateWorkflowFileUseCase;
|
|
4
|
+
exports.createValidateWorkflowFileUseCasePipeline = createValidateWorkflowFileUseCasePipeline;
|
|
5
|
+
const workflow_validation_pipeline_js_1 = require("../services/workflow-validation-pipeline.js");
|
|
4
6
|
function createValidateWorkflowFileUseCase(deps) {
|
|
5
7
|
return function validateWorkflowFile(filePath) {
|
|
6
8
|
const resolvedPath = deps.resolvePath(filePath);
|
|
@@ -55,3 +57,97 @@ function createValidateWorkflowFileUseCase(deps) {
|
|
|
55
57
|
};
|
|
56
58
|
};
|
|
57
59
|
}
|
|
60
|
+
function createValidateWorkflowFileUseCasePipeline(deps) {
|
|
61
|
+
return function validateWorkflowFilePipeline(filePath) {
|
|
62
|
+
const resolvedPath = deps.resolvePath(filePath);
|
|
63
|
+
if (!deps.existsSync(resolvedPath)) {
|
|
64
|
+
return { kind: 'file_not_found', filePath };
|
|
65
|
+
}
|
|
66
|
+
let content;
|
|
67
|
+
try {
|
|
68
|
+
content = deps.readFileSyncUtf8(resolvedPath);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return {
|
|
72
|
+
kind: 'read_error',
|
|
73
|
+
filePath,
|
|
74
|
+
message: err?.message ?? String(err),
|
|
75
|
+
code: err?.code,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
let parsed;
|
|
79
|
+
try {
|
|
80
|
+
parsed = deps.parseJson(content);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { kind: 'json_parse_error', filePath, message: err?.message ?? String(err) };
|
|
84
|
+
}
|
|
85
|
+
const definition = parsed;
|
|
86
|
+
const runtimeWorkflow = deps.makeRuntimeWorkflow(definition, resolvedPath);
|
|
87
|
+
if (deps.validationPipelineDeps) {
|
|
88
|
+
const outcome = (0, workflow_validation_pipeline_js_1.validateWorkflowPhase1a)(runtimeWorkflow, deps.validationPipelineDeps);
|
|
89
|
+
switch (outcome.kind) {
|
|
90
|
+
case 'schema_failed':
|
|
91
|
+
return {
|
|
92
|
+
kind: 'schema_invalid',
|
|
93
|
+
filePath,
|
|
94
|
+
errors: outcome.errors.map(e => e.message || `Schema error at ${e.instancePath}`),
|
|
95
|
+
};
|
|
96
|
+
case 'structural_failed':
|
|
97
|
+
return {
|
|
98
|
+
kind: 'structural_invalid',
|
|
99
|
+
filePath,
|
|
100
|
+
issues: outcome.issues,
|
|
101
|
+
};
|
|
102
|
+
case 'v1_compilation_failed':
|
|
103
|
+
return {
|
|
104
|
+
kind: 'v1_compilation_failed',
|
|
105
|
+
filePath,
|
|
106
|
+
message: outcome.cause.message,
|
|
107
|
+
};
|
|
108
|
+
case 'normalization_failed':
|
|
109
|
+
return {
|
|
110
|
+
kind: 'normalization_failed',
|
|
111
|
+
filePath,
|
|
112
|
+
message: outcome.cause.message,
|
|
113
|
+
};
|
|
114
|
+
case 'executable_compilation_failed':
|
|
115
|
+
return {
|
|
116
|
+
kind: 'executable_compilation_failed',
|
|
117
|
+
filePath,
|
|
118
|
+
message: outcome.cause.message,
|
|
119
|
+
};
|
|
120
|
+
case 'phase1a_valid':
|
|
121
|
+
return {
|
|
122
|
+
kind: 'valid',
|
|
123
|
+
filePath,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const schemaResult = deps.schemaValidate(definition);
|
|
128
|
+
if (!schemaResult.valid) {
|
|
129
|
+
return { kind: 'schema_invalid', filePath, errors: schemaResult.errors };
|
|
130
|
+
}
|
|
131
|
+
const structural = deps.validateRuntimeWorkflow(runtimeWorkflow);
|
|
132
|
+
if (structural.valid) {
|
|
133
|
+
const warnings = structural.warnings?.length ? structural.warnings : undefined;
|
|
134
|
+
const info = structural.info?.length ? structural.info : undefined;
|
|
135
|
+
const suggestions = structural.suggestions.length ? structural.suggestions : undefined;
|
|
136
|
+
return {
|
|
137
|
+
kind: 'valid',
|
|
138
|
+
filePath,
|
|
139
|
+
warnings,
|
|
140
|
+
info,
|
|
141
|
+
suggestions,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
kind: 'structural_invalid',
|
|
146
|
+
filePath,
|
|
147
|
+
issues: structural.issues,
|
|
148
|
+
warnings: structural.warnings,
|
|
149
|
+
info: structural.info,
|
|
150
|
+
suggestions: structural.suggestions.length ? structural.suggestions : undefined,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type ValidationPipelineDepsPhase1a } from '../services/workflow-validation-pipeline.js';
|
|
1
2
|
export interface WorkflowJsonValidationResult {
|
|
2
3
|
valid: boolean;
|
|
3
4
|
issues: string[];
|
|
4
5
|
suggestions: string[];
|
|
5
6
|
}
|
|
6
|
-
export declare function createValidateWorkflowJson(): (workflowJson: string) => Promise<WorkflowJsonValidationResult>;
|
|
7
|
+
export declare function createValidateWorkflowJson(deps?: ValidationPipelineDepsPhase1a): (workflowJson: string) => Promise<WorkflowJsonValidationResult>;
|
|
7
8
|
export declare function validateWorkflowJson(workflowJson: string): Promise<WorkflowJsonValidationResult>;
|
|
@@ -2,8 +2,63 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createValidateWorkflowJson = createValidateWorkflowJson;
|
|
4
4
|
exports.validateWorkflowJson = validateWorkflowJson;
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const validation_js_1 = require("../validation.js");
|
|
6
|
+
const workflow_validation_pipeline_js_1 = require("../services/workflow-validation-pipeline.js");
|
|
7
|
+
const validation_engine_js_1 = require("../services/validation-engine.js");
|
|
8
|
+
const enhanced_loop_validator_js_1 = require("../services/enhanced-loop-validator.js");
|
|
9
|
+
const workflow_compiler_js_1 = require("../services/workflow-compiler.js");
|
|
10
|
+
const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
|
|
11
|
+
const workflow_js_1 = require("../../types/workflow.js");
|
|
12
|
+
const workflow_source_js_1 = require("../../types/workflow-source.js");
|
|
13
|
+
function buildDefaultPipelineDeps() {
|
|
14
|
+
const loopValidator = new enhanced_loop_validator_js_1.EnhancedLoopValidator();
|
|
15
|
+
const validationEngine = new validation_engine_js_1.ValidationEngine(loopValidator);
|
|
16
|
+
const compiler = new workflow_compiler_js_1.WorkflowCompiler();
|
|
17
|
+
return {
|
|
18
|
+
schemaValidate: validation_js_1.validateWorkflowSchema,
|
|
19
|
+
structuralValidate: validationEngine.validateWorkflowStructureOnly.bind(validationEngine),
|
|
20
|
+
compiler,
|
|
21
|
+
normalizeToExecutable: v1_to_v2_shim_js_1.normalizeV1WorkflowToPinnedSnapshot,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function mapOutcomeToResult(outcome) {
|
|
25
|
+
switch (outcome.kind) {
|
|
26
|
+
case 'schema_failed':
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
issues: outcome.errors.map(e => e.message ?? e.keyword ?? 'Schema validation error').filter(Boolean),
|
|
30
|
+
suggestions: generateSuggestions(outcome.errors.map(e => e.message ?? '').filter(Boolean)),
|
|
31
|
+
};
|
|
32
|
+
case 'structural_failed':
|
|
33
|
+
return {
|
|
34
|
+
valid: false,
|
|
35
|
+
issues: outcome.issues.slice(),
|
|
36
|
+
suggestions: generateSuggestions(outcome.issues.slice()),
|
|
37
|
+
};
|
|
38
|
+
case 'v1_compilation_failed':
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
issues: [`Compilation error: ${outcome.cause.message}`],
|
|
42
|
+
suggestions: ['Check step references, loop definitions, and prompt sources.'],
|
|
43
|
+
};
|
|
44
|
+
case 'normalization_failed':
|
|
45
|
+
return {
|
|
46
|
+
valid: false,
|
|
47
|
+
issues: [`Normalization error: ${outcome.cause.message}`],
|
|
48
|
+
suggestions: ['Check templateCall references, promptBlocks, and step definitions.'],
|
|
49
|
+
};
|
|
50
|
+
case 'executable_compilation_failed':
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
issues: [`Executable compilation error: ${outcome.cause.message}`],
|
|
54
|
+
suggestions: ['The normalized workflow has an internal conflict. Check that steps use exactly one of prompt, promptBlocks, or templateCall.'],
|
|
55
|
+
};
|
|
56
|
+
case 'phase1a_valid':
|
|
57
|
+
return { valid: true, issues: [], suggestions: [] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function createValidateWorkflowJson(deps) {
|
|
61
|
+
const pipelineDeps = deps ?? buildDefaultPipelineDeps();
|
|
7
62
|
return async (workflowJson) => {
|
|
8
63
|
if (workflowJson === null || workflowJson === undefined || typeof workflowJson !== 'string') {
|
|
9
64
|
return {
|
|
@@ -37,18 +92,17 @@ function createValidateWorkflowJson() {
|
|
|
37
92
|
]
|
|
38
93
|
};
|
|
39
94
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
if (typeof parsedWorkflow !== 'object' || parsedWorkflow === null || Array.isArray(parsedWorkflow)) {
|
|
96
|
+
return {
|
|
97
|
+
valid: false,
|
|
98
|
+
issues: ['Workflow JSON must be an object, not a primitive or array.'],
|
|
99
|
+
suggestions: ['Provide a JSON object with fields: id, name, description, version, steps.'],
|
|
100
|
+
};
|
|
46
101
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
};
|
|
102
|
+
const definition = parsedWorkflow;
|
|
103
|
+
const workflow = (0, workflow_js_1.createWorkflow)(definition, (0, workflow_source_js_1.createBundledSource)());
|
|
104
|
+
const outcome = (0, workflow_validation_pipeline_js_1.validateWorkflowPhase1a)(workflow, pipelineDeps);
|
|
105
|
+
return mapOutcomeToResult(outcome);
|
|
52
106
|
};
|
|
53
107
|
}
|
|
54
108
|
function generateSuggestions(errors) {
|