@exaudeus/workrail 0.12.0 → 0.14.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/enhanced-loop-validator.js +3 -3
- package/dist/application/services/step-output-decoder.d.ts +6 -0
- package/dist/application/services/step-output-decoder.js +49 -0
- package/dist/application/services/validation-engine.d.ts +9 -0
- package/dist/application/services/validation-engine.js +142 -18
- package/dist/application/services/workflow-interpreter.d.ts +1 -1
- package/dist/application/services/workflow-interpreter.js +147 -81
- package/dist/application/services/workflow-service.d.ts +2 -0
- package/dist/application/services/workflow-service.js +9 -4
- package/dist/application/use-cases/validate-step-output.d.ts +2 -0
- package/dist/di/container.js +19 -3
- package/dist/di/tokens.d.ts +3 -0
- package/dist/di/tokens.js +3 -0
- package/dist/domain/execution/state.d.ts +6 -6
- package/dist/domain/workflow-id-policy.d.ts +17 -0
- package/dist/domain/workflow-id-policy.js +57 -0
- package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.js +33 -6
- package/dist/infrastructure/storage/file-workflow-storage.js +3 -1
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +13 -8
- package/dist/manifest.json +261 -109
- package/dist/mcp/handlers/v2-execution.js +62 -79
- package/dist/mcp/handlers/v2-workflow.js +14 -9
- package/dist/mcp/server.js +53 -48
- package/dist/mcp/tool-descriptions.js +15 -15
- package/dist/mcp/tools.js +14 -14
- package/dist/mcp/types/tool-description-types.d.ts +1 -1
- package/dist/mcp/types/tool-description-types.js +5 -5
- package/dist/mcp/types.d.ts +2 -0
- package/dist/v2/durable-core/constants.d.ts +15 -0
- package/dist/v2/durable-core/constants.js +18 -0
- package/dist/v2/durable-core/domain/ack-advance-append-plan.d.ts +32 -0
- package/dist/v2/durable-core/domain/ack-advance-append-plan.js +95 -0
- package/dist/v2/durable-core/domain/loop-runtime.d.ts +50 -0
- package/dist/v2/durable-core/domain/loop-runtime.js +95 -0
- package/dist/v2/durable-core/domain/notes-markdown.d.ts +4 -0
- package/dist/v2/durable-core/domain/notes-markdown.js +46 -0
- package/dist/v2/durable-core/domain/outputs.d.ts +12 -0
- package/dist/v2/durable-core/domain/outputs.js +18 -0
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +113 -113
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.js +11 -10
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +7129 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.js +82 -0
- package/dist/v2/durable-core/schemas/lib/decision-trace-ref.d.ts +80 -0
- package/dist/v2/durable-core/schemas/lib/decision-trace-ref.js +38 -0
- package/dist/v2/durable-core/schemas/lib/dedupe-key.d.ts +8 -0
- package/dist/v2/durable-core/schemas/lib/dedupe-key.js +28 -0
- package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.d.ts +6 -0
- package/dist/v2/durable-core/schemas/lib/utf8-bounded-string.js +12 -0
- package/dist/v2/durable-core/schemas/session/events.d.ts +158 -12
- package/dist/v2/durable-core/schemas/session/events.js +47 -20
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +1 -1
- package/dist/v2/durable-core/schemas/session/manifest.js +6 -1
- package/dist/v2/durable-core/schemas/session/preferences.d.ts +5 -0
- package/dist/v2/durable-core/schemas/session/preferences.js +6 -0
- package/dist/v2/durable-core/schemas/session/session-health.d.ts +3 -0
- package/dist/v2/durable-core/tokens/index.d.ts +0 -1
- package/dist/v2/durable-core/tokens/index.js +1 -4
- package/dist/v2/durable-core/tokens/token-codec.d.ts +3 -2
- package/dist/v2/durable-core/tokens/token-codec.js +12 -6
- package/dist/v2/durable-core/tokens/token-signer.d.ts +3 -2
- package/dist/v2/durable-core/tokens/token-signer.js +8 -9
- package/dist/v2/infra/local/base64url/index.d.ts +5 -0
- package/dist/v2/infra/local/base64url/index.js +48 -0
- package/dist/v2/infra/local/fs/index.js +8 -4
- package/dist/v2/infra/local/keyring/index.d.ts +5 -1
- package/dist/v2/infra/local/keyring/index.js +41 -32
- package/dist/v2/infra/local/pinned-workflow-store/index.d.ts +3 -1
- package/dist/v2/infra/local/pinned-workflow-store/index.js +50 -62
- package/dist/v2/infra/local/random-entropy/index.d.ts +4 -0
- package/dist/v2/infra/local/random-entropy/index.js +10 -0
- package/dist/v2/infra/local/session-lock/index.d.ts +3 -1
- package/dist/v2/infra/local/session-lock/index.js +4 -3
- package/dist/v2/infra/local/session-store/index.js +26 -4
- package/dist/v2/infra/local/snapshot-store/index.js +20 -25
- package/dist/v2/infra/local/time-clock/index.d.ts +5 -0
- package/dist/v2/infra/local/time-clock/index.js +12 -0
- package/dist/v2/infra/local/utf8/index.d.ts +5 -0
- package/dist/v2/infra/local/utf8/index.js +12 -0
- package/dist/v2/ports/base64url.port.d.ts +12 -0
- package/dist/v2/ports/base64url.port.js +2 -0
- package/dist/v2/ports/random-entropy.port.d.ts +3 -0
- package/dist/v2/ports/random-entropy.port.js +2 -0
- package/dist/v2/ports/time-clock.port.d.ts +4 -0
- package/dist/v2/ports/time-clock.port.js +2 -0
- package/dist/v2/ports/utf8.port.d.ts +3 -0
- package/dist/v2/ports/utf8.port.js +2 -0
- package/dist/v2/projections/node-outputs.js +28 -11
- package/dist/v2/projections/preferences.d.ts +1 -2
- package/dist/v2/projections/preferences.js +11 -4
- package/dist/v2/projections/run-dag.js +40 -28
- package/dist/v2/projections/run-status-signals.d.ts +1 -2
- package/dist/v2/usecases/execution-session-gate.js +33 -34
- package/package.json +3 -1
- package/spec/workflow.schema.json +2 -2
- package/workflows/coding-task-workflow-agentic.json +213 -50
- package/workflows/relocation-workflow-us.json +430 -0
- package/dist/v2/durable-core/tokens/base64url.d.ts +0 -7
- package/dist/v2/durable-core/tokens/base64url.js +0 -16
|
@@ -102,10 +102,10 @@ let EnhancedLoopValidator = class EnhancedLoopValidator {
|
|
|
102
102
|
}
|
|
103
103
|
getKnownLoopVariables(loopStep) {
|
|
104
104
|
const vars = new Set();
|
|
105
|
-
vars.add(loopStep.loop.iterationVar || '
|
|
105
|
+
vars.add(loopStep.loop.iterationVar || 'currentIteration');
|
|
106
106
|
if (loopStep.loop.type === 'forEach') {
|
|
107
|
-
vars.add(loopStep.loop.itemVar || '
|
|
108
|
-
vars.add(loopStep.loop.indexVar || '
|
|
107
|
+
vars.add(loopStep.loop.itemVar || 'currentItem');
|
|
108
|
+
vars.add(loopStep.loop.indexVar || 'currentIndex');
|
|
109
109
|
}
|
|
110
110
|
vars.add('context');
|
|
111
111
|
vars.add('workflowId');
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface SchemaDecodeResult {
|
|
2
|
+
readonly value: unknown;
|
|
3
|
+
readonly warnings: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function decodeForSchemaValidation(output: string, schema: unknown): SchemaDecodeResult | null;
|
|
6
|
+
export declare function schemaExpectsObjectOrArray(schema: unknown): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeForSchemaValidation = decodeForSchemaValidation;
|
|
4
|
+
exports.schemaExpectsObjectOrArray = schemaExpectsObjectOrArray;
|
|
5
|
+
const COERCION_WARNING = 'Coerced double-encoded JSON (JSON string containing JSON) into an object/array for schema validation.';
|
|
6
|
+
function decodeForSchemaValidation(output, schema) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(output);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (typeof parsed === 'string' && schemaExpectsObjectOrArray(schema)) {
|
|
15
|
+
const inner = parsed.trim();
|
|
16
|
+
if (inner.startsWith('{') || inner.startsWith('[')) {
|
|
17
|
+
try {
|
|
18
|
+
const innerParsed = JSON.parse(inner);
|
|
19
|
+
if (innerParsed !== null && typeof innerParsed === 'object') {
|
|
20
|
+
return { value: innerParsed, warnings: [COERCION_WARNING] };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { value: parsed, warnings: [] };
|
|
28
|
+
}
|
|
29
|
+
function schemaExpectsObjectOrArray(schema) {
|
|
30
|
+
if (schema == null || typeof schema !== 'object')
|
|
31
|
+
return false;
|
|
32
|
+
const s = schema;
|
|
33
|
+
const t = s.type;
|
|
34
|
+
if (t === 'object' || t === 'array')
|
|
35
|
+
return true;
|
|
36
|
+
if (Array.isArray(t) && (t.includes('object') || t.includes('array')))
|
|
37
|
+
return true;
|
|
38
|
+
if (s.properties && typeof s.properties === 'object')
|
|
39
|
+
return true;
|
|
40
|
+
if (Array.isArray(s.required) && s.required.length > 0)
|
|
41
|
+
return true;
|
|
42
|
+
const combos = ['oneOf', 'anyOf', 'allOf'];
|
|
43
|
+
for (const k of combos) {
|
|
44
|
+
if (Array.isArray(s[k]) && s[k].some((child) => schemaExpectsObjectOrArray(child))) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
@@ -7,6 +7,9 @@ export declare class ValidationEngine {
|
|
|
7
7
|
private ajv;
|
|
8
8
|
private schemaCache;
|
|
9
9
|
private enhancedLoopValidator;
|
|
10
|
+
private static readonly DEFAULT_FAILURE_SUGGESTION;
|
|
11
|
+
private static readonly JSON_OBJECT_NOT_STRING_SUGGESTION;
|
|
12
|
+
private static readonly JSON_STRING_CONTAINS_JSON_SUGGESTION;
|
|
10
13
|
constructor(enhancedLoopValidator: EnhancedLoopValidator);
|
|
11
14
|
private compileSchema;
|
|
12
15
|
private evaluateCriteria;
|
|
@@ -17,8 +20,14 @@ export declare class ValidationEngine {
|
|
|
17
20
|
private isValidationComposition;
|
|
18
21
|
validate(output: string, criteria: ValidationCriteria, context?: ConditionContext): Promise<ValidationResult>;
|
|
19
22
|
private evaluateRule;
|
|
23
|
+
private looksLikeQuotedJsonSnippet;
|
|
24
|
+
private maybePushJsonObjectNotStringSuggestion;
|
|
25
|
+
private uniqueSuggestions;
|
|
26
|
+
private formatDefaultContainsMessage;
|
|
20
27
|
validateLoopStep(step: LoopStepDefinition, workflow: Workflow): ValidationResult;
|
|
21
28
|
validateWorkflow(workflow: Workflow): ValidationResult;
|
|
29
|
+
private collectQuotedJsonValidationMessageWarnings;
|
|
30
|
+
private collectValidationRuleMessages;
|
|
22
31
|
private isValidVariableName;
|
|
23
32
|
private validateStepFunctionCalls;
|
|
24
33
|
private isArgTypeValid;
|
|
@@ -14,6 +14,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
15
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
16
|
};
|
|
17
|
+
var ValidationEngine_1;
|
|
17
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
19
|
exports.ValidationEngine = void 0;
|
|
19
20
|
const tsyringe_1 = require("tsyringe");
|
|
@@ -22,7 +23,8 @@ const condition_evaluator_1 = require("../../utils/condition-evaluator");
|
|
|
22
23
|
const ajv_1 = __importDefault(require("ajv"));
|
|
23
24
|
const workflow_definition_1 = require("../../types/workflow-definition");
|
|
24
25
|
const enhanced_loop_validator_1 = require("./enhanced-loop-validator");
|
|
25
|
-
|
|
26
|
+
const step_output_decoder_1 = require("./step-output-decoder");
|
|
27
|
+
let ValidationEngine = ValidationEngine_1 = class ValidationEngine {
|
|
26
28
|
constructor(enhancedLoopValidator) {
|
|
27
29
|
this.schemaCache = new Map();
|
|
28
30
|
this.ajv = new ajv_1.default({ allErrors: true });
|
|
@@ -52,7 +54,8 @@ let ValidationEngine = class ValidationEngine {
|
|
|
52
54
|
return {
|
|
53
55
|
valid: compositionResult,
|
|
54
56
|
issues: compositionResult ? [] : ['Validation composition failed'],
|
|
55
|
-
suggestions: compositionResult ? [] : ['Review validation criteria and adjust output accordingly.']
|
|
57
|
+
suggestions: compositionResult ? [] : ['Review validation criteria and adjust output accordingly.'],
|
|
58
|
+
warnings: undefined,
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
61
|
throw new error_handler_1.ValidationError('Invalid validation criteria format');
|
|
@@ -66,12 +69,14 @@ let ValidationEngine = class ValidationEngine {
|
|
|
66
69
|
}
|
|
67
70
|
evaluateRuleArray(output, rules, context) {
|
|
68
71
|
const issues = [];
|
|
72
|
+
const suggestions = [];
|
|
73
|
+
const warnings = [];
|
|
69
74
|
for (const rule of rules) {
|
|
70
75
|
try {
|
|
71
76
|
if (rule.condition && !(0, condition_evaluator_1.evaluateCondition)(rule.condition, context)) {
|
|
72
77
|
continue;
|
|
73
78
|
}
|
|
74
|
-
this.evaluateRule(output, rule, issues);
|
|
79
|
+
this.evaluateRule(output, rule, issues, suggestions, warnings);
|
|
75
80
|
}
|
|
76
81
|
catch (error) {
|
|
77
82
|
if (error instanceof error_handler_1.ValidationError) {
|
|
@@ -83,7 +88,8 @@ let ValidationEngine = class ValidationEngine {
|
|
|
83
88
|
return {
|
|
84
89
|
valid: issues.length === 0,
|
|
85
90
|
issues,
|
|
86
|
-
suggestions: issues.length > 0 ? [
|
|
91
|
+
suggestions: issues.length > 0 ? this.uniqueSuggestions([...suggestions, ValidationEngine_1.DEFAULT_FAILURE_SUGGESTION]) : [],
|
|
92
|
+
warnings: warnings.length > 0 ? this.uniqueSuggestions(warnings) : undefined,
|
|
87
93
|
};
|
|
88
94
|
}
|
|
89
95
|
evaluateComposition(output, composition, context) {
|
|
@@ -104,7 +110,9 @@ let ValidationEngine = class ValidationEngine {
|
|
|
104
110
|
return true;
|
|
105
111
|
}
|
|
106
112
|
const issues = [];
|
|
107
|
-
|
|
113
|
+
const suggestions = [];
|
|
114
|
+
const warnings = [];
|
|
115
|
+
this.evaluateRule(output, criteria, issues, suggestions, warnings);
|
|
108
116
|
return issues.length === 0;
|
|
109
117
|
}
|
|
110
118
|
if (this.isValidationComposition(criteria)) {
|
|
@@ -133,18 +141,19 @@ let ValidationEngine = class ValidationEngine {
|
|
|
133
141
|
suggestions: issues.length > 0 ? ['Provide valid output content.'] : []
|
|
134
142
|
};
|
|
135
143
|
}
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
138
|
-
issues.push(...
|
|
144
|
+
const evaluation = this.evaluateCriteria(output, criteria, context || {});
|
|
145
|
+
if (!evaluation.valid) {
|
|
146
|
+
issues.push(...evaluation.issues);
|
|
139
147
|
}
|
|
140
148
|
const valid = issues.length === 0;
|
|
141
149
|
return {
|
|
142
150
|
valid,
|
|
143
151
|
issues,
|
|
144
|
-
suggestions: valid ? [] : [
|
|
152
|
+
suggestions: valid ? [] : this.uniqueSuggestions([...evaluation.suggestions, ValidationEngine_1.DEFAULT_FAILURE_SUGGESTION]),
|
|
153
|
+
warnings: evaluation.warnings,
|
|
145
154
|
};
|
|
146
155
|
}
|
|
147
|
-
evaluateRule(output, rule, issues) {
|
|
156
|
+
evaluateRule(output, rule, issues, suggestions, warnings) {
|
|
148
157
|
if (typeof rule === 'string') {
|
|
149
158
|
const re = new RegExp(rule);
|
|
150
159
|
if (!re.test(output)) {
|
|
@@ -157,7 +166,10 @@ let ValidationEngine = class ValidationEngine {
|
|
|
157
166
|
case 'contains': {
|
|
158
167
|
const value = rule.value;
|
|
159
168
|
if (typeof value !== 'string' || !output.includes(value)) {
|
|
160
|
-
issues.push(rule.message ||
|
|
169
|
+
issues.push(rule.message || this.formatDefaultContainsMessage(value));
|
|
170
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
171
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
172
|
+
}
|
|
161
173
|
}
|
|
162
174
|
break;
|
|
163
175
|
}
|
|
@@ -166,6 +178,9 @@ let ValidationEngine = class ValidationEngine {
|
|
|
166
178
|
const re = new RegExp(rule.pattern, rule.flags || undefined);
|
|
167
179
|
if (!re.test(output)) {
|
|
168
180
|
issues.push(rule.message || `Pattern mismatch: ${rule.pattern}`);
|
|
181
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
182
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
183
|
+
}
|
|
169
184
|
}
|
|
170
185
|
}
|
|
171
186
|
catch {
|
|
@@ -177,31 +192,50 @@ let ValidationEngine = class ValidationEngine {
|
|
|
177
192
|
const { min, max } = rule;
|
|
178
193
|
if (typeof min === 'number' && output.length < min) {
|
|
179
194
|
issues.push(rule.message || `Output shorter than minimum length ${min}`);
|
|
195
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
196
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
197
|
+
}
|
|
180
198
|
}
|
|
181
199
|
if (typeof max === 'number' && output.length > max) {
|
|
182
200
|
issues.push(rule.message || `Output exceeds maximum length ${max}`);
|
|
201
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
202
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
203
|
+
}
|
|
183
204
|
}
|
|
184
205
|
break;
|
|
185
206
|
}
|
|
186
207
|
case 'schema': {
|
|
187
208
|
if (!rule.schema) {
|
|
188
209
|
issues.push(rule.message || 'Schema validation rule requires a schema property');
|
|
210
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
211
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
212
|
+
}
|
|
189
213
|
break;
|
|
190
214
|
}
|
|
191
215
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
parsedOutput = JSON.parse(output);
|
|
195
|
-
}
|
|
196
|
-
catch (parseError) {
|
|
216
|
+
const decoded = (0, step_output_decoder_1.decodeForSchemaValidation)(output, rule.schema);
|
|
217
|
+
if (!decoded) {
|
|
197
218
|
issues.push(rule.message || 'Output is not valid JSON for schema validation');
|
|
219
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
220
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
221
|
+
}
|
|
198
222
|
break;
|
|
199
223
|
}
|
|
224
|
+
if (decoded.warnings.length > 0) {
|
|
225
|
+
warnings.push(...decoded.warnings);
|
|
226
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
227
|
+
if (!suggestions.includes(ValidationEngine_1.JSON_STRING_CONTAINS_JSON_SUGGESTION)) {
|
|
228
|
+
suggestions.push(ValidationEngine_1.JSON_STRING_CONTAINS_JSON_SUGGESTION);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
200
231
|
const validate = this.compileSchema(rule.schema);
|
|
201
|
-
const isValid = validate(
|
|
232
|
+
const isValid = validate(decoded.value);
|
|
202
233
|
if (!isValid) {
|
|
203
234
|
const errorMessages = validate.errors?.map((error) => `Validation Error at '${error.instancePath}': ${error.message}`) || ['Schema validation failed'];
|
|
204
235
|
issues.push(rule.message || errorMessages.join('; '));
|
|
236
|
+
if (this.looksLikeQuotedJsonSnippet(rule.message)) {
|
|
237
|
+
this.maybePushJsonObjectNotStringSuggestion(suggestions);
|
|
238
|
+
}
|
|
205
239
|
}
|
|
206
240
|
}
|
|
207
241
|
catch (error) {
|
|
@@ -219,6 +253,50 @@ let ValidationEngine = class ValidationEngine {
|
|
|
219
253
|
}
|
|
220
254
|
throw new error_handler_1.ValidationError('Invalid validationCriteria format.');
|
|
221
255
|
}
|
|
256
|
+
looksLikeQuotedJsonSnippet(message) {
|
|
257
|
+
if (typeof message !== 'string')
|
|
258
|
+
return false;
|
|
259
|
+
const m = message.trim();
|
|
260
|
+
if (m.length === 0)
|
|
261
|
+
return false;
|
|
262
|
+
if (m.includes('\\"{') || m.includes('\\"['))
|
|
263
|
+
return true;
|
|
264
|
+
if (m.includes('"{') || m.includes("'{") || m.includes('`{'))
|
|
265
|
+
return true;
|
|
266
|
+
if (m.includes('"[') || m.includes("'[") || m.includes('`['))
|
|
267
|
+
return true;
|
|
268
|
+
return /["'`]\s*[\[{]/.test(m);
|
|
269
|
+
}
|
|
270
|
+
maybePushJsonObjectNotStringSuggestion(suggestions) {
|
|
271
|
+
if (suggestions.includes(ValidationEngine_1.JSON_OBJECT_NOT_STRING_SUGGESTION))
|
|
272
|
+
return;
|
|
273
|
+
suggestions.push(ValidationEngine_1.JSON_OBJECT_NOT_STRING_SUGGESTION);
|
|
274
|
+
}
|
|
275
|
+
uniqueSuggestions(values) {
|
|
276
|
+
const out = [];
|
|
277
|
+
for (const v of values) {
|
|
278
|
+
if (!v)
|
|
279
|
+
continue;
|
|
280
|
+
if (out.includes(v))
|
|
281
|
+
continue;
|
|
282
|
+
out.push(v);
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
formatDefaultContainsMessage(value) {
|
|
287
|
+
if (typeof value !== 'string') {
|
|
288
|
+
return 'Output must include the required substring.';
|
|
289
|
+
}
|
|
290
|
+
const snippet = value.trim();
|
|
291
|
+
const looksJsonLike = snippet.startsWith('{') ||
|
|
292
|
+
snippet.startsWith('[') ||
|
|
293
|
+
snippet.includes('"') ||
|
|
294
|
+
snippet.includes(':');
|
|
295
|
+
if (looksJsonLike) {
|
|
296
|
+
return `Output must include this literal JSON snippet (not a JSON-encoded string): ${snippet}`;
|
|
297
|
+
}
|
|
298
|
+
return `Output must include: ${snippet}`;
|
|
299
|
+
}
|
|
222
300
|
validateLoopStep(step, workflow) {
|
|
223
301
|
const enhancedResult = this.enhancedLoopValidator.validateLoopStep(step);
|
|
224
302
|
const issues = [];
|
|
@@ -366,6 +444,7 @@ let ValidationEngine = class ValidationEngine {
|
|
|
366
444
|
if (loopResult.info) {
|
|
367
445
|
info.push(...loopResult.info.map(i => `Step '${step.id}': ${i}`));
|
|
368
446
|
}
|
|
447
|
+
this.collectQuotedJsonValidationMessageWarnings(step, `Step '${step.id}'`, warnings);
|
|
369
448
|
}
|
|
370
449
|
else {
|
|
371
450
|
if (!step.id) {
|
|
@@ -377,6 +456,7 @@ let ValidationEngine = class ValidationEngine {
|
|
|
377
456
|
if (!step.prompt) {
|
|
378
457
|
issues.push(`Step '${step.id}' missing required prompt`);
|
|
379
458
|
}
|
|
459
|
+
this.collectQuotedJsonValidationMessageWarnings(step, `Step '${step.id}'`, warnings);
|
|
380
460
|
const callValidation = this.validateStepFunctionCalls(step, workflow.definition.functionDefinitions || []);
|
|
381
461
|
if (!callValidation.valid) {
|
|
382
462
|
issues.push(...callValidation.issues.map(i => `Step '${step.id}': ${i}`));
|
|
@@ -413,6 +493,47 @@ let ValidationEngine = class ValidationEngine {
|
|
|
413
493
|
info: info.length > 0 ? info : undefined
|
|
414
494
|
};
|
|
415
495
|
}
|
|
496
|
+
collectQuotedJsonValidationMessageWarnings(stepLike, prefix, warnings) {
|
|
497
|
+
const criteria = stepLike?.validationCriteria;
|
|
498
|
+
if (!criteria)
|
|
499
|
+
return;
|
|
500
|
+
const msgs = this.collectValidationRuleMessages(criteria);
|
|
501
|
+
for (const m of msgs) {
|
|
502
|
+
if (!this.looksLikeQuotedJsonSnippet(m))
|
|
503
|
+
continue;
|
|
504
|
+
warnings.push(`${prefix}: validationCriteria.message appears to contain a quoted JSON snippet. ` +
|
|
505
|
+
`Agents often respond with a JSON-encoded string (escaping quotes) instead of a JSON object. ` +
|
|
506
|
+
`Prefer describing the required object/keys without quoting the JSON (or use schema validation).`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
collectValidationRuleMessages(criteria) {
|
|
511
|
+
if (Array.isArray(criteria)) {
|
|
512
|
+
const msgs = [];
|
|
513
|
+
for (const r of criteria) {
|
|
514
|
+
if (r && typeof r === 'object' && typeof r.message === 'string')
|
|
515
|
+
msgs.push(r.message);
|
|
516
|
+
}
|
|
517
|
+
return msgs;
|
|
518
|
+
}
|
|
519
|
+
if (this.isValidationRule(criteria)) {
|
|
520
|
+
return typeof criteria.message === 'string' ? [String(criteria.message)] : [];
|
|
521
|
+
}
|
|
522
|
+
if (this.isValidationComposition(criteria)) {
|
|
523
|
+
const msgs = [];
|
|
524
|
+
const c = criteria;
|
|
525
|
+
if (Array.isArray(c.and))
|
|
526
|
+
for (const child of c.and)
|
|
527
|
+
msgs.push(...this.collectValidationRuleMessages(child));
|
|
528
|
+
if (Array.isArray(c.or))
|
|
529
|
+
for (const child of c.or)
|
|
530
|
+
msgs.push(...this.collectValidationRuleMessages(child));
|
|
531
|
+
if (c.not)
|
|
532
|
+
msgs.push(...this.collectValidationRuleMessages(c.not));
|
|
533
|
+
return msgs;
|
|
534
|
+
}
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
416
537
|
isValidVariableName(name) {
|
|
417
538
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
418
539
|
}
|
|
@@ -472,7 +593,10 @@ let ValidationEngine = class ValidationEngine {
|
|
|
472
593
|
}
|
|
473
594
|
};
|
|
474
595
|
exports.ValidationEngine = ValidationEngine;
|
|
475
|
-
|
|
596
|
+
ValidationEngine.DEFAULT_FAILURE_SUGGESTION = 'Review validation criteria and adjust output accordingly.';
|
|
597
|
+
ValidationEngine.JSON_OBJECT_NOT_STRING_SUGGESTION = 'If you are returning JSON, return an object/array directly (not a JSON-encoded string). Do not wrap it in quotes or escape quotes.';
|
|
598
|
+
ValidationEngine.JSON_STRING_CONTAINS_JSON_SUGGESTION = 'It looks like you returned a JSON string that itself contains JSON. Remove the outer quotes and return the JSON object/array directly.';
|
|
599
|
+
exports.ValidationEngine = ValidationEngine = ValidationEngine_1 = __decorate([
|
|
476
600
|
(0, tsyringe_1.singleton)(),
|
|
477
601
|
__param(0, (0, tsyringe_1.inject)(enhanced_loop_validator_1.EnhancedLoopValidator)),
|
|
478
602
|
__metadata("design:paramtypes", [enhanced_loop_validator_1.EnhancedLoopValidator])
|
|
@@ -24,7 +24,7 @@ export declare class WorkflowInterpreter {
|
|
|
24
24
|
private ensureRunning;
|
|
25
25
|
private nextTopLevel;
|
|
26
26
|
private nextInCurrentLoop;
|
|
27
|
-
private
|
|
27
|
+
private projectLoopContextAtIteration;
|
|
28
28
|
private projectLoopContext;
|
|
29
29
|
private lookupStepInstance;
|
|
30
30
|
private materializeStep;
|