@exaudeus/workrail 3.10.0 → 3.11.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/validation-engine.js +134 -0
- package/dist/application/services/workflow-compiler.js +54 -0
- package/dist/manifest.json +138 -74
- package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +14 -0
- package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +27 -0
- package/dist/mcp/handlers/v2-advance-core/assessment-validation.d.ts +16 -0
- package/dist/mcp/handlers/v2-advance-core/assessment-validation.js +213 -0
- package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +1 -0
- package/dist/mcp/handlers/v2-advance-core/event-builders.js +3 -2
- package/dist/mcp/handlers/v2-advance-core/index.js +23 -8
- package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +9 -1
- package/dist/mcp/handlers/v2-advance-core/input-validation.js +22 -2
- package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
- package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +69 -19
- package/dist/mcp/handlers/v2-advance-core/outcome-success.js +22 -0
- package/dist/mcp/handlers/v2-execution/replay.js +7 -0
- package/dist/mcp/output-schemas.d.ts +156 -42
- package/dist/mcp/output-schemas.js +15 -0
- package/dist/mcp/v2-response-formatter.js +7 -1
- package/dist/types/workflow-definition.d.ts +26 -0
- package/dist/types/workflow-definition.js +16 -1
- package/dist/v2/durable-core/constants.d.ts +2 -0
- package/dist/v2/durable-core/constants.js +2 -0
- package/dist/v2/durable-core/domain/assessment-consequence-event-builder.d.ts +23 -0
- package/dist/v2/durable-core/domain/assessment-consequence-event-builder.js +36 -0
- package/dist/v2/durable-core/domain/assessment-record.d.ts +12 -0
- package/dist/v2/durable-core/domain/assessment-record.js +2 -0
- package/dist/v2/durable-core/domain/assessment-recorded-event-builder.d.ts +22 -0
- package/dist/v2/durable-core/domain/assessment-recorded-event-builder.js +38 -0
- package/dist/v2/durable-core/domain/blocked-node-builder.d.ts +1 -1
- package/dist/v2/durable-core/domain/blocked-node-builder.js +8 -0
- package/dist/v2/durable-core/domain/blocking-decision.d.ts +6 -0
- package/dist/v2/durable-core/domain/blocking-decision.js +15 -0
- package/dist/v2/durable-core/domain/prompt-renderer.js +25 -1
- package/dist/v2/durable-core/domain/reason-model.d.ts +12 -2
- package/dist/v2/durable-core/domain/reason-model.js +27 -2
- package/dist/v2/durable-core/domain/risk-policy-guardrails.js +1 -0
- package/dist/v2/durable-core/domain/validation-criteria-validator.d.ts +1 -0
- package/dist/v2/durable-core/domain/validation-criteria-validator.js +11 -0
- package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +55 -0
- package/dist/v2/durable-core/schemas/artifacts/assessment.js +29 -0
- package/dist/v2/durable-core/schemas/artifacts/index.d.ts +2 -1
- package/dist/v2/durable-core/schemas/artifacts/index.js +8 -1
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +24 -24
- package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +141 -21
- package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.js +10 -1
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +729 -171
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +1442 -202
- package/dist/v2/durable-core/schemas/session/events.d.ts +231 -8
- package/dist/v2/durable-core/schemas/session/events.js +36 -0
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +2 -2
- package/dist/v2/projections/assessment-consequences.d.ts +19 -0
- package/dist/v2/projections/assessment-consequences.js +33 -0
- package/dist/v2/projections/assessments.d.ts +21 -0
- package/dist/v2/projections/assessments.js +35 -0
- package/package.json +1 -1
- package/spec/workflow.schema.json +110 -0
- package/workflows/bug-investigation.agentic.v2.json +28 -2
- package/workflows/test-artifact-loop-control.json +28 -2
|
@@ -144,6 +144,11 @@ const V2BlockerPointerSchema = zod_1.z.discriminatedUnion('kind', [
|
|
|
144
144
|
zod_1.z.object({ kind: zod_1.z.literal('context_budget') }),
|
|
145
145
|
zod_1.z.object({ kind: zod_1.z.literal('output_contract'), contractRef: zod_1.z.string().min(1) }),
|
|
146
146
|
zod_1.z.object({ kind: zod_1.z.literal('capability'), capability: zod_1.z.enum(['delegation', 'web_browsing']) }),
|
|
147
|
+
zod_1.z.object({
|
|
148
|
+
kind: zod_1.z.literal('assessment_dimension'),
|
|
149
|
+
assessmentId: zod_1.z.string().min(1),
|
|
150
|
+
dimensionId: zod_1.z.string().min(1).regex(DELIMITER_SAFE_ID_PATTERN, 'dimensionId must be delimiter-safe: [a-z0-9_-]+'),
|
|
151
|
+
}),
|
|
147
152
|
zod_1.z.object({
|
|
148
153
|
kind: zod_1.z.literal('workflow_step'),
|
|
149
154
|
stepId: zod_1.z.string().min(1).regex(DELIMITER_SAFE_ID_PATTERN, 'stepId must be delimiter-safe: [a-z0-9_-]+'),
|
|
@@ -154,6 +159,7 @@ const V2BlockerSchema = zod_1.z.object({
|
|
|
154
159
|
'USER_ONLY_DEPENDENCY',
|
|
155
160
|
'MISSING_REQUIRED_OUTPUT',
|
|
156
161
|
'INVALID_REQUIRED_OUTPUT',
|
|
162
|
+
'ASSESSMENT_FOLLOWUP_REQUIRED',
|
|
157
163
|
'MISSING_REQUIRED_NOTES',
|
|
158
164
|
'MISSING_CONTEXT_KEY',
|
|
159
165
|
'CONTEXT_BUDGET_EXCEEDED',
|
|
@@ -195,6 +201,9 @@ exports.V2BlockerReportSchema = zod_1.z
|
|
|
195
201
|
case 'capability':
|
|
196
202
|
ptrStable = p.capability;
|
|
197
203
|
break;
|
|
204
|
+
case 'assessment_dimension':
|
|
205
|
+
ptrStable = `${p.assessmentId}|${p.dimensionId}`;
|
|
206
|
+
break;
|
|
198
207
|
case 'workflow_step':
|
|
199
208
|
ptrStable = p.stepId;
|
|
200
209
|
break;
|
|
@@ -252,6 +261,12 @@ const V2ContinueWorkflowBlockedSchema = zod_1.z.object({
|
|
|
252
261
|
suggestions: zod_1.z.array(zod_1.z.string()),
|
|
253
262
|
})
|
|
254
263
|
.optional(),
|
|
264
|
+
assessmentFollowup: zod_1.z
|
|
265
|
+
.object({
|
|
266
|
+
title: zod_1.z.string().min(1),
|
|
267
|
+
guidance: zod_1.z.string().min(1),
|
|
268
|
+
})
|
|
269
|
+
.optional(),
|
|
255
270
|
});
|
|
256
271
|
exports.V2ContinueWorkflowOutputSchema = zod_1.z.discriminatedUnion('kind', [
|
|
257
272
|
V2ContinueWorkflowOkSchema,
|
|
@@ -126,7 +126,13 @@ function formatBlockedRetryable(data) {
|
|
|
126
126
|
lines.push('');
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
if (data.assessmentFollowup) {
|
|
130
|
+
lines.push('**Follow-up required before retrying this step:**');
|
|
131
|
+
lines.push(`- ${data.assessmentFollowup.title}`);
|
|
132
|
+
lines.push(`- ${data.assessmentFollowup.guidance}`);
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
135
|
+
lines.push('Retry the same step with improved output:');
|
|
130
136
|
lines.push('');
|
|
131
137
|
lines.push(formatTokenBlock(data));
|
|
132
138
|
return lines.join('\n');
|
|
@@ -11,6 +11,29 @@ export interface PromptFragment {
|
|
|
11
11
|
readonly when?: Condition;
|
|
12
12
|
readonly text: string;
|
|
13
13
|
}
|
|
14
|
+
export interface AssessmentDimensionDefinition {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly purpose: string;
|
|
17
|
+
readonly levels: readonly string[];
|
|
18
|
+
readonly required?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface AssessmentDefinition {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly purpose: string;
|
|
23
|
+
readonly dimensions: readonly AssessmentDimensionDefinition[];
|
|
24
|
+
}
|
|
25
|
+
export interface AssessmentConsequenceTriggerDefinition {
|
|
26
|
+
readonly dimensionId: string;
|
|
27
|
+
readonly equalsLevel: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AssessmentFollowupRequiredEffectDefinition {
|
|
30
|
+
readonly kind: 'require_followup';
|
|
31
|
+
readonly guidance: string;
|
|
32
|
+
}
|
|
33
|
+
export interface AssessmentConsequenceDefinition {
|
|
34
|
+
readonly when: AssessmentConsequenceTriggerDefinition;
|
|
35
|
+
readonly effect: AssessmentFollowupRequiredEffectDefinition;
|
|
36
|
+
}
|
|
14
37
|
export interface WorkflowStepDefinition {
|
|
15
38
|
readonly id: string;
|
|
16
39
|
readonly title: string;
|
|
@@ -23,6 +46,8 @@ export interface WorkflowStepDefinition {
|
|
|
23
46
|
readonly runCondition?: Readonly<Record<string, unknown>>;
|
|
24
47
|
readonly validationCriteria?: ValidationCriteria;
|
|
25
48
|
readonly outputContract?: OutputContract;
|
|
49
|
+
readonly assessmentRefs?: readonly string[];
|
|
50
|
+
readonly assessmentConsequences?: readonly AssessmentConsequenceDefinition[];
|
|
26
51
|
readonly notesOptional?: boolean;
|
|
27
52
|
readonly templateCall?: TemplateCall;
|
|
28
53
|
readonly promptFragments?: readonly PromptFragment[];
|
|
@@ -106,6 +131,7 @@ export interface WorkflowDefinition {
|
|
|
106
131
|
readonly functionDefinitions?: readonly FunctionDefinition[];
|
|
107
132
|
readonly recommendedPreferences?: WorkflowRecommendedPreferences;
|
|
108
133
|
readonly features?: readonly string[];
|
|
134
|
+
readonly assessments?: readonly AssessmentDefinition[];
|
|
109
135
|
readonly extensionPoints?: readonly ExtensionPoint[];
|
|
110
136
|
readonly references?: readonly WorkflowReference[];
|
|
111
137
|
}
|
|
@@ -28,11 +28,26 @@ function hasWorkflowDefinitionShape(obj) {
|
|
|
28
28
|
function createWorkflowDefinition(definition) {
|
|
29
29
|
return Object.freeze({
|
|
30
30
|
...definition,
|
|
31
|
-
steps: Object.freeze(definition.steps.map(step => Object.freeze({
|
|
31
|
+
steps: Object.freeze(definition.steps.map(step => Object.freeze({
|
|
32
|
+
...step,
|
|
33
|
+
assessmentConsequences: 'assessmentConsequences' in step && step.assessmentConsequences
|
|
34
|
+
? Object.freeze(step.assessmentConsequences.map(consequence => Object.freeze({
|
|
35
|
+
...consequence,
|
|
36
|
+
when: Object.freeze({ ...consequence.when }),
|
|
37
|
+
effect: Object.freeze({ ...consequence.effect }),
|
|
38
|
+
})))
|
|
39
|
+
: undefined,
|
|
40
|
+
}))),
|
|
32
41
|
preconditions: definition.preconditions ? Object.freeze([...definition.preconditions]) : undefined,
|
|
33
42
|
clarificationPrompts: definition.clarificationPrompts ? Object.freeze([...definition.clarificationPrompts]) : undefined,
|
|
34
43
|
metaGuidance: definition.metaGuidance ? Object.freeze([...definition.metaGuidance]) : undefined,
|
|
35
44
|
functionDefinitions: definition.functionDefinitions ? Object.freeze([...definition.functionDefinitions]) : undefined,
|
|
45
|
+
assessments: definition.assessments
|
|
46
|
+
? Object.freeze(definition.assessments.map(assessment => Object.freeze({
|
|
47
|
+
...assessment,
|
|
48
|
+
dimensions: Object.freeze(assessment.dimensions.map(dimension => Object.freeze({ ...dimension }))),
|
|
49
|
+
})))
|
|
50
|
+
: undefined,
|
|
36
51
|
extensionPoints: definition.extensionPoints ? Object.freeze([...definition.extensionPoints]) : undefined,
|
|
37
52
|
references: definition.references ? Object.freeze(definition.references.map(ref => Object.freeze({ ...ref }))) : undefined,
|
|
38
53
|
});
|
|
@@ -28,6 +28,8 @@ export declare const EVENT_KIND: {
|
|
|
28
28
|
readonly ADVANCE_RECORDED: "advance_recorded";
|
|
29
29
|
readonly VALIDATION_PERFORMED: "validation_performed";
|
|
30
30
|
readonly NODE_OUTPUT_APPENDED: "node_output_appended";
|
|
31
|
+
readonly ASSESSMENT_RECORDED: "assessment_recorded";
|
|
32
|
+
readonly ASSESSMENT_CONSEQUENCE_APPLIED: "assessment_consequence_applied";
|
|
31
33
|
readonly PREFERENCES_CHANGED: "preferences_changed";
|
|
32
34
|
readonly CAPABILITY_OBSERVED: "capability_observed";
|
|
33
35
|
readonly GAP_RECORDED: "gap_recorded";
|
|
@@ -31,6 +31,8 @@ exports.EVENT_KIND = {
|
|
|
31
31
|
ADVANCE_RECORDED: 'advance_recorded',
|
|
32
32
|
VALIDATION_PERFORMED: 'validation_performed',
|
|
33
33
|
NODE_OUTPUT_APPENDED: 'node_output_appended',
|
|
34
|
+
ASSESSMENT_RECORDED: 'assessment_recorded',
|
|
35
|
+
ASSESSMENT_CONSEQUENCE_APPLIED: 'assessment_consequence_applied',
|
|
34
36
|
PREFERENCES_CHANGED: 'preferences_changed',
|
|
35
37
|
CAPABILITY_OBSERVED: 'capability_observed',
|
|
36
38
|
GAP_RECORDED: 'gap_recorded',
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Result } from 'neverthrow';
|
|
2
|
+
import type { DomainEventV1 } from '../schemas/session/index.js';
|
|
3
|
+
type EventToAppendV1 = Omit<DomainEventV1, 'eventIndex' | 'sessionId'>;
|
|
4
|
+
export type AssessmentConsequenceEventError = {
|
|
5
|
+
readonly code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION';
|
|
6
|
+
readonly message: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function buildAssessmentConsequenceAppliedEvent(args: {
|
|
9
|
+
readonly sessionId: string;
|
|
10
|
+
readonly attemptId: string;
|
|
11
|
+
readonly scope: {
|
|
12
|
+
readonly runId: string;
|
|
13
|
+
readonly nodeId: string;
|
|
14
|
+
};
|
|
15
|
+
readonly assessmentId: string;
|
|
16
|
+
readonly dimensionId: string;
|
|
17
|
+
readonly level: string;
|
|
18
|
+
readonly guidance: string;
|
|
19
|
+
readonly minted: {
|
|
20
|
+
readonly eventId: string;
|
|
21
|
+
};
|
|
22
|
+
}): Result<EventToAppendV1, AssessmentConsequenceEventError>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAssessmentConsequenceAppliedEvent = buildAssessmentConsequenceAppliedEvent;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const constants_js_1 = require("../constants.js");
|
|
6
|
+
function buildAssessmentConsequenceAppliedEvent(args) {
|
|
7
|
+
if (!args.sessionId)
|
|
8
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'sessionId is required' });
|
|
9
|
+
if (!args.attemptId)
|
|
10
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'attemptId is required' });
|
|
11
|
+
if (!args.scope.runId || !args.scope.nodeId) {
|
|
12
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'scope.runId and scope.nodeId are required' });
|
|
13
|
+
}
|
|
14
|
+
if (!args.assessmentId || !args.dimensionId || !args.level || !args.guidance) {
|
|
15
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'assessment consequence fields are required' });
|
|
16
|
+
}
|
|
17
|
+
return (0, neverthrow_1.ok)({
|
|
18
|
+
v: 1,
|
|
19
|
+
eventId: args.minted.eventId,
|
|
20
|
+
kind: constants_js_1.EVENT_KIND.ASSESSMENT_CONSEQUENCE_APPLIED,
|
|
21
|
+
dedupeKey: `assessment_consequence_applied:${args.sessionId}:${args.scope.nodeId}:${args.attemptId}`,
|
|
22
|
+
scope: { runId: args.scope.runId, nodeId: args.scope.nodeId },
|
|
23
|
+
data: {
|
|
24
|
+
attemptId: args.attemptId,
|
|
25
|
+
assessmentId: args.assessmentId,
|
|
26
|
+
trigger: {
|
|
27
|
+
dimensionId: args.dimensionId,
|
|
28
|
+
level: args.level,
|
|
29
|
+
},
|
|
30
|
+
effect: {
|
|
31
|
+
kind: 'require_followup',
|
|
32
|
+
guidance: args.guidance,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface RecordedAssessmentDimensionV1 {
|
|
2
|
+
readonly dimensionId: string;
|
|
3
|
+
readonly level: string;
|
|
4
|
+
readonly rationale?: string;
|
|
5
|
+
readonly normalization: 'exact' | 'normalized';
|
|
6
|
+
}
|
|
7
|
+
export interface RecordedAssessmentV1 {
|
|
8
|
+
readonly assessmentId: string;
|
|
9
|
+
readonly summary?: string;
|
|
10
|
+
readonly dimensions: readonly RecordedAssessmentDimensionV1[];
|
|
11
|
+
readonly normalizationNotes: readonly string[];
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Result } from 'neverthrow';
|
|
2
|
+
import type { DomainEventV1 } from '../schemas/session/index.js';
|
|
3
|
+
import type { RecordedAssessmentV1 } from './assessment-record.js';
|
|
4
|
+
type EventToAppendV1 = Omit<DomainEventV1, 'eventIndex' | 'sessionId'>;
|
|
5
|
+
export type AssessmentRecordedEventError = {
|
|
6
|
+
readonly code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION';
|
|
7
|
+
readonly message: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildAssessmentRecordedEvent(args: {
|
|
10
|
+
readonly sessionId: string;
|
|
11
|
+
readonly attemptId: string;
|
|
12
|
+
readonly artifactOutputId: string;
|
|
13
|
+
readonly scope: {
|
|
14
|
+
readonly runId: string;
|
|
15
|
+
readonly nodeId: string;
|
|
16
|
+
};
|
|
17
|
+
readonly assessment: RecordedAssessmentV1;
|
|
18
|
+
readonly minted: {
|
|
19
|
+
readonly eventId: string;
|
|
20
|
+
};
|
|
21
|
+
}): Result<EventToAppendV1, AssessmentRecordedEventError>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAssessmentRecordedEvent = buildAssessmentRecordedEvent;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const constants_js_1 = require("../constants.js");
|
|
6
|
+
function buildAssessmentRecordedEvent(args) {
|
|
7
|
+
if (!args.sessionId)
|
|
8
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'sessionId is required' });
|
|
9
|
+
if (!args.attemptId)
|
|
10
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'attemptId is required' });
|
|
11
|
+
if (!args.artifactOutputId)
|
|
12
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'artifactOutputId is required' });
|
|
13
|
+
if (!args.scope.runId || !args.scope.nodeId) {
|
|
14
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'scope.runId and scope.nodeId are required' });
|
|
15
|
+
}
|
|
16
|
+
if (!args.minted.eventId)
|
|
17
|
+
return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'minted.eventId is required' });
|
|
18
|
+
return (0, neverthrow_1.ok)({
|
|
19
|
+
v: 1,
|
|
20
|
+
eventId: args.minted.eventId,
|
|
21
|
+
kind: constants_js_1.EVENT_KIND.ASSESSMENT_RECORDED,
|
|
22
|
+
dedupeKey: `assessment_recorded:${args.sessionId}:${args.scope.nodeId}:${args.attemptId}`,
|
|
23
|
+
scope: { runId: args.scope.runId, nodeId: args.scope.nodeId },
|
|
24
|
+
data: {
|
|
25
|
+
assessmentId: args.assessment.assessmentId,
|
|
26
|
+
attemptId: args.attemptId,
|
|
27
|
+
artifactOutputId: args.artifactOutputId,
|
|
28
|
+
summary: args.assessment.summary,
|
|
29
|
+
normalizationNotes: [...args.assessment.normalizationNotes],
|
|
30
|
+
dimensions: args.assessment.dimensions.map((dimension) => ({
|
|
31
|
+
dimensionId: dimension.dimensionId,
|
|
32
|
+
level: dimension.level,
|
|
33
|
+
normalization: dimension.normalization,
|
|
34
|
+
...(dimension.rationale ? { rationale: dimension.rationale } : {}),
|
|
35
|
+
})),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -14,7 +14,7 @@ export declare function buildBlockedNodeSnapshot(args: {
|
|
|
14
14
|
readonly priorSnapshot: ExecutionSnapshotFileV1;
|
|
15
15
|
readonly primaryReason: ReasonV1;
|
|
16
16
|
readonly attemptId: AttemptId;
|
|
17
|
-
readonly validationRef
|
|
17
|
+
readonly validationRef?: string;
|
|
18
18
|
readonly blockers: BlockerReportV1;
|
|
19
19
|
readonly sha256: Sha256PortV2;
|
|
20
20
|
}): Result<ExecutionSnapshotFileV1, BlockedNodeBuildError>;
|
|
@@ -9,6 +9,14 @@ function toContractViolationReason(reason) {
|
|
|
9
9
|
return { kind: 'invalid_required_output', contractRef: reason.contractRef };
|
|
10
10
|
case 'missing_required_output':
|
|
11
11
|
return { kind: 'missing_required_output', contractRef: reason.contractRef };
|
|
12
|
+
case 'assessment_followup_required':
|
|
13
|
+
return {
|
|
14
|
+
kind: 'assessment_followup_required',
|
|
15
|
+
assessmentId: reason.assessmentId,
|
|
16
|
+
dimensionId: reason.dimensionId,
|
|
17
|
+
level: reason.level,
|
|
18
|
+
guidance: reason.guidance,
|
|
19
|
+
};
|
|
12
20
|
case 'missing_context_key':
|
|
13
21
|
return { kind: 'missing_context_key', key: reason.key };
|
|
14
22
|
case 'context_budget_exceeded':
|
|
@@ -34,4 +34,10 @@ export declare function detectBlockingReasonsV1(args: {
|
|
|
34
34
|
readonly missingNotes?: {
|
|
35
35
|
readonly stepId: string;
|
|
36
36
|
};
|
|
37
|
+
readonly assessmentFollowupRequired?: {
|
|
38
|
+
readonly assessmentId: string;
|
|
39
|
+
readonly dimensionId: string;
|
|
40
|
+
readonly level: string;
|
|
41
|
+
readonly guidance: string;
|
|
42
|
+
};
|
|
37
43
|
}): Result<readonly ReasonV1[], BlockingDecisionError>;
|
|
@@ -63,5 +63,20 @@ function detectBlockingReasonsV1(args) {
|
|
|
63
63
|
}
|
|
64
64
|
reasons.push({ kind: 'missing_notes', stepId: args.missingNotes.stepId });
|
|
65
65
|
}
|
|
66
|
+
if (args.assessmentFollowupRequired) {
|
|
67
|
+
if (!constants_js_1.DELIMITER_SAFE_ID_PATTERN.test(args.assessmentFollowupRequired.dimensionId)) {
|
|
68
|
+
return (0, neverthrow_1.err)({
|
|
69
|
+
code: 'INVALID_DELIMITER_SAFE_ID',
|
|
70
|
+
message: `assessment dimension ID must be delimiter-safe: [a-z0-9_-]+ (got: ${args.assessmentFollowupRequired.dimensionId})`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
reasons.push({
|
|
74
|
+
kind: 'assessment_followup_required',
|
|
75
|
+
assessmentId: args.assessmentFollowupRequired.assessmentId,
|
|
76
|
+
dimensionId: args.assessmentFollowupRequired.dimensionId,
|
|
77
|
+
level: args.assessmentFollowupRequired.level,
|
|
78
|
+
guidance: args.assessmentFollowupRequired.guidance,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
66
81
|
return (0, neverthrow_1.ok)(reasons);
|
|
67
82
|
}
|
|
@@ -181,6 +181,18 @@ function formatOutputContractRequirements(outputContract) {
|
|
|
181
181
|
];
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
function formatAssessmentRequirements(assessments) {
|
|
185
|
+
if (assessments.length === 0)
|
|
186
|
+
return [];
|
|
187
|
+
const requirements = [];
|
|
188
|
+
for (const assessment of assessments) {
|
|
189
|
+
requirements.push('Provide an artifact with kind: "wr.assessment"');
|
|
190
|
+
requirements.push(`Assessment target: "${assessment.id}"`);
|
|
191
|
+
requirements.push(`Dimensions: ${assessment.dimensions.map((dimension) => `${dimension.id} (${dimension.levels.join(' | ')})`).join(', ')}`);
|
|
192
|
+
requirements.push('Use only canonical dimension levels. If the engine rejects the artifact, correct the submitted levels instead of inventing new ones.');
|
|
193
|
+
}
|
|
194
|
+
return requirements;
|
|
195
|
+
}
|
|
184
196
|
function assembleFragmentedPrompt(fragments, context) {
|
|
185
197
|
return fragments
|
|
186
198
|
.filter(f => (0, condition_evaluator_js_1.evaluateCondition)(f.when, context))
|
|
@@ -217,6 +229,12 @@ function renderPendingPrompt(args) {
|
|
|
217
229
|
const outputContract = 'outputContract' in step
|
|
218
230
|
? step.outputContract
|
|
219
231
|
: undefined;
|
|
232
|
+
const stepAssessmentRefs = 'assessmentRefs' in step
|
|
233
|
+
? step.assessmentRefs
|
|
234
|
+
: undefined;
|
|
235
|
+
const stepAssessments = stepAssessmentRefs && stepAssessmentRefs.length > 0
|
|
236
|
+
? (args.workflow.definition.assessments ?? []).filter((assessment) => stepAssessmentRefs.includes(assessment.id))
|
|
237
|
+
: [];
|
|
220
238
|
const isExitStep = outputContract?.contractRef === index_js_2.LOOP_CONTROL_CONTRACT_REF;
|
|
221
239
|
const loopStep = resolveParentLoopStep(args.workflow, args.stepId);
|
|
222
240
|
const maxIterations = loopStep?.loop.maxIterations;
|
|
@@ -247,6 +265,12 @@ function renderPendingPrompt(args) {
|
|
|
247
265
|
? `\n\n${contractRequirements.map(r => `- ${r}`).join('\n')}`
|
|
248
266
|
: `\n\n**OUTPUT REQUIREMENTS (System):**\n${contractRequirements.map(r => `- ${r}`).join('\n')}`
|
|
249
267
|
: '';
|
|
268
|
+
const assessmentRequirements = formatAssessmentRequirements(stepAssessments);
|
|
269
|
+
const assessmentSection = assessmentRequirements.length > 0
|
|
270
|
+
? cleanResponseFormat
|
|
271
|
+
? `\n\n${assessmentRequirements.map(r => `- ${r}`).join('\n')}`
|
|
272
|
+
: `\n\n**ASSESSMENT REQUIREMENTS (System):**\n${assessmentRequirements.map(r => `- ${r}`).join('\n')}`
|
|
273
|
+
: '';
|
|
250
274
|
const isNotesOptional = outputContract !== undefined ||
|
|
251
275
|
('notesOptional' in step && step.notesOptional === true);
|
|
252
276
|
const notesSection = (() => {
|
|
@@ -297,7 +321,7 @@ function renderPendingPrompt(args) {
|
|
|
297
321
|
const fragmentSuffix = promptFragments && promptFragments.length > 0
|
|
298
322
|
? assembleFragmentedPrompt(promptFragments, renderContext)
|
|
299
323
|
: '';
|
|
300
|
-
const enhancedPrompt = loopBanner + basePrompt + requirementsSection + contractSection + notesSection
|
|
324
|
+
const enhancedPrompt = loopBanner + basePrompt + requirementsSection + contractSection + assessmentSection + notesSection
|
|
301
325
|
+ (fragmentSuffix ? '\n\n' + fragmentSuffix : '');
|
|
302
326
|
if (!args.rehydrateOnly) {
|
|
303
327
|
return (0, neverthrow_1.ok)({ stepId: args.stepId, title: baseTitle, prompt: enhancedPrompt, agentRole, requireConfirmation });
|
|
@@ -8,7 +8,7 @@ export type GapReasonV1 = {
|
|
|
8
8
|
readonly detail: UserOnlyDependencyReasonV1;
|
|
9
9
|
} | {
|
|
10
10
|
readonly category: 'contract_violation';
|
|
11
|
-
readonly detail: 'missing_required_output' | 'invalid_required_output' | 'missing_required_notes';
|
|
11
|
+
readonly detail: 'missing_required_output' | 'invalid_required_output' | 'missing_required_notes' | 'assessment_followup_required';
|
|
12
12
|
} | {
|
|
13
13
|
readonly category: 'capability_missing';
|
|
14
14
|
readonly detail: 'required_capability_unavailable' | 'required_capability_unknown';
|
|
@@ -16,7 +16,7 @@ export type GapReasonV1 = {
|
|
|
16
16
|
readonly category: 'unexpected';
|
|
17
17
|
readonly detail: 'invariant_violation' | 'storage_corruption_detected' | 'evaluation_error';
|
|
18
18
|
};
|
|
19
|
-
export type BlockerCodeV1 = 'USER_ONLY_DEPENDENCY' | 'MISSING_REQUIRED_OUTPUT' | 'INVALID_REQUIRED_OUTPUT' | 'MISSING_REQUIRED_NOTES' | 'MISSING_CONTEXT_KEY' | 'CONTEXT_BUDGET_EXCEEDED' | 'REQUIRED_CAPABILITY_UNKNOWN' | 'REQUIRED_CAPABILITY_UNAVAILABLE' | 'INVARIANT_VIOLATION' | 'STORAGE_CORRUPTION_DETECTED';
|
|
19
|
+
export type BlockerCodeV1 = 'USER_ONLY_DEPENDENCY' | 'MISSING_REQUIRED_OUTPUT' | 'INVALID_REQUIRED_OUTPUT' | 'ASSESSMENT_FOLLOWUP_REQUIRED' | 'MISSING_REQUIRED_NOTES' | 'MISSING_CONTEXT_KEY' | 'CONTEXT_BUDGET_EXCEEDED' | 'REQUIRED_CAPABILITY_UNKNOWN' | 'REQUIRED_CAPABILITY_UNAVAILABLE' | 'INVARIANT_VIOLATION' | 'STORAGE_CORRUPTION_DETECTED';
|
|
20
20
|
export type BlockerPointerV1 = {
|
|
21
21
|
readonly kind: 'context_key';
|
|
22
22
|
readonly key: string;
|
|
@@ -28,6 +28,10 @@ export type BlockerPointerV1 = {
|
|
|
28
28
|
} | {
|
|
29
29
|
readonly kind: 'capability';
|
|
30
30
|
readonly capability: CapabilityV2;
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: 'assessment_dimension';
|
|
33
|
+
readonly assessmentId: string;
|
|
34
|
+
readonly dimensionId: string;
|
|
31
35
|
} | {
|
|
32
36
|
readonly kind: 'workflow_step';
|
|
33
37
|
readonly stepId: string;
|
|
@@ -52,6 +56,12 @@ export type ReasonV1 = {
|
|
|
52
56
|
} | {
|
|
53
57
|
readonly kind: 'invalid_required_output';
|
|
54
58
|
readonly contractRef: string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'assessment_followup_required';
|
|
61
|
+
readonly assessmentId: string;
|
|
62
|
+
readonly dimensionId: string;
|
|
63
|
+
readonly level: string;
|
|
64
|
+
readonly guidance: string;
|
|
55
65
|
} | {
|
|
56
66
|
readonly kind: 'missing_notes';
|
|
57
67
|
readonly stepId: string;
|
|
@@ -8,6 +8,7 @@ exports.shouldBlock = shouldBlock;
|
|
|
8
8
|
const constants_js_1 = require("../constants.js");
|
|
9
9
|
const neverthrow_1 = require("neverthrow");
|
|
10
10
|
const utf8_byte_length_js_1 = require("../schemas/lib/utf8-byte-length.js");
|
|
11
|
+
const index_js_1 = require("../schemas/artifacts/index.js");
|
|
11
12
|
function ensureDelimiterSafeId(label, value) {
|
|
12
13
|
if (!constants_js_1.DELIMITER_SAFE_ID_PATTERN.test(value)) {
|
|
13
14
|
return (0, neverthrow_1.err)({
|
|
@@ -58,6 +59,12 @@ function reasonToGap(reason) {
|
|
|
58
59
|
reason: { category: 'contract_violation', detail: 'invalid_required_output' },
|
|
59
60
|
summary: `Invalid required output for contractRef=${reason.contractRef}`,
|
|
60
61
|
};
|
|
62
|
+
case 'assessment_followup_required':
|
|
63
|
+
return {
|
|
64
|
+
severity: 'critical',
|
|
65
|
+
reason: { category: 'contract_violation', detail: 'assessment_followup_required' },
|
|
66
|
+
summary: `Assessment follow-up required: ${reason.assessmentId}.${reason.dimensionId} == ${reason.level}`,
|
|
67
|
+
};
|
|
61
68
|
case 'missing_notes':
|
|
62
69
|
return {
|
|
63
70
|
severity: 'critical',
|
|
@@ -125,6 +132,9 @@ function blockerSortKey(b) {
|
|
|
125
132
|
case 'capability':
|
|
126
133
|
ptrStable = p.capability;
|
|
127
134
|
break;
|
|
135
|
+
case 'assessment_dimension':
|
|
136
|
+
ptrStable = `${p.assessmentId}|${p.dimensionId}`;
|
|
137
|
+
break;
|
|
128
138
|
case 'workflow_step':
|
|
129
139
|
ptrStable = p.stepId;
|
|
130
140
|
break;
|
|
@@ -162,7 +172,9 @@ function reasonToBlocker(reason) {
|
|
|
162
172
|
code: 'MISSING_REQUIRED_OUTPUT',
|
|
163
173
|
pointer: { kind: 'output_contract', contractRef },
|
|
164
174
|
message: `Missing required output (contractRef=${contractRef}).`,
|
|
165
|
-
suggestedFix:
|
|
175
|
+
suggestedFix: contractRef === index_js_1.ASSESSMENT_CONTRACT_REF
|
|
176
|
+
? 'Call continue_workflow without output to rehydrate the current step, then retry with an assessment artifact under output.artifacts using kind "wr.assessment" and the required canonical dimension levels.'
|
|
177
|
+
: 'Call continue_workflow without output to rehydrate the current step, then retry with output.notesMarkdown that satisfies the step output requirements.',
|
|
166
178
|
}))
|
|
167
179
|
.andThen(ensureBlockerTextBudgets);
|
|
168
180
|
case 'invalid_required_output':
|
|
@@ -171,9 +183,22 @@ function reasonToBlocker(reason) {
|
|
|
171
183
|
code: 'INVALID_REQUIRED_OUTPUT',
|
|
172
184
|
pointer: { kind: 'output_contract', contractRef },
|
|
173
185
|
message: `Invalid output for contractRef=${contractRef}.`,
|
|
174
|
-
suggestedFix:
|
|
186
|
+
suggestedFix: contractRef === index_js_1.ASSESSMENT_CONTRACT_REF
|
|
187
|
+
? 'Update the assessment artifact in output.artifacts so it uses the expected assessment ID and canonical dimension levels. Then call continue_workflow without output to rehydrate the current step and retry with the corrected artifact.'
|
|
188
|
+
: 'Update output.notesMarkdown to satisfy validation. Then call continue_workflow without output to rehydrate the current step and retry advance with the corrected output. Replaying the same invalid advance will keep returning this blocked result.',
|
|
175
189
|
}))
|
|
176
190
|
.andThen(ensureBlockerTextBudgets);
|
|
191
|
+
case 'assessment_followup_required':
|
|
192
|
+
return ensureBlockerTextBudgets({
|
|
193
|
+
code: 'ASSESSMENT_FOLLOWUP_REQUIRED',
|
|
194
|
+
pointer: {
|
|
195
|
+
kind: 'assessment_dimension',
|
|
196
|
+
assessmentId: reason.assessmentId,
|
|
197
|
+
dimensionId: reason.dimensionId,
|
|
198
|
+
},
|
|
199
|
+
message: `Follow-up required before this step can proceed: ${reason.assessmentId}.${reason.dimensionId} matched "${reason.level}".`,
|
|
200
|
+
suggestedFix: `Stay on this step, address this follow-up, and retry with updated output: ${reason.guidance}`,
|
|
201
|
+
});
|
|
177
202
|
case 'missing_notes':
|
|
178
203
|
return ensureDelimiterSafeId('workflow_step.stepId', reason.stepId)
|
|
179
204
|
.map((stepId) => ({
|
|
@@ -11,6 +11,7 @@ export declare function getOutputRequirementStatusWithArtifactsV1(args: {
|
|
|
11
11
|
readonly outputContract: OutputContract | undefined;
|
|
12
12
|
readonly artifacts: readonly unknown[];
|
|
13
13
|
readonly validationCriteria: ValidationCriteria | undefined;
|
|
14
|
+
readonly assessmentValidation?: ValidationResult;
|
|
14
15
|
readonly notesMarkdown: string | undefined;
|
|
15
16
|
readonly validation: ValidationResult | undefined;
|
|
16
17
|
}): OutputRequirementStatus;
|
|
@@ -4,6 +4,7 @@ exports.VALIDATION_CRITERIA_CONTRACT_REF = void 0;
|
|
|
4
4
|
exports.getOutputRequirementStatusV1 = getOutputRequirementStatusV1;
|
|
5
5
|
exports.getOutputRequirementStatusWithArtifactsV1 = getOutputRequirementStatusWithArtifactsV1;
|
|
6
6
|
const artifact_contract_validator_js_1 = require("./artifact-contract-validator.js");
|
|
7
|
+
const index_js_1 = require("../schemas/artifacts/index.js");
|
|
7
8
|
exports.VALIDATION_CRITERIA_CONTRACT_REF = 'wr.validationCriteria';
|
|
8
9
|
function getOutputRequirementStatusV1(args) {
|
|
9
10
|
if (!args.validationCriteria)
|
|
@@ -38,6 +39,16 @@ function getOutputRequirementStatusWithArtifactsV1(args) {
|
|
|
38
39
|
}
|
|
39
40
|
return { kind: 'satisfied' };
|
|
40
41
|
}
|
|
42
|
+
if (args.assessmentValidation) {
|
|
43
|
+
if (!args.assessmentValidation.valid) {
|
|
44
|
+
return {
|
|
45
|
+
kind: 'invalid',
|
|
46
|
+
contractRef: index_js_1.ASSESSMENT_CONTRACT_REF,
|
|
47
|
+
validation: args.assessmentValidation,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { kind: 'satisfied' };
|
|
51
|
+
}
|
|
41
52
|
return getOutputRequirementStatusV1({
|
|
42
53
|
validationCriteria: args.validationCriteria,
|
|
43
54
|
notesMarkdown: args.notesMarkdown,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ASSESSMENT_CONTRACT_REF: "wr.contracts.assessment";
|
|
3
|
+
export declare const AssessmentDimensionSubmissionSchema: z.ZodUnion<[z.ZodString, z.ZodObject<{
|
|
4
|
+
level: z.ZodString;
|
|
5
|
+
rationale: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
level: string;
|
|
8
|
+
rationale?: string | undefined;
|
|
9
|
+
}, {
|
|
10
|
+
level: string;
|
|
11
|
+
rationale?: string | undefined;
|
|
12
|
+
}>]>;
|
|
13
|
+
export type AssessmentDimensionSubmission = z.infer<typeof AssessmentDimensionSubmissionSchema>;
|
|
14
|
+
export declare const AssessmentArtifactV1Schema: z.ZodObject<{
|
|
15
|
+
kind: z.ZodLiteral<"wr.assessment">;
|
|
16
|
+
assessmentId: z.ZodOptional<z.ZodString>;
|
|
17
|
+
dimensions: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
|
|
18
|
+
level: z.ZodString;
|
|
19
|
+
rationale: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, "strict", z.ZodTypeAny, {
|
|
21
|
+
level: string;
|
|
22
|
+
rationale?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
level: string;
|
|
25
|
+
rationale?: string | undefined;
|
|
26
|
+
}>]>>, Record<string, string | {
|
|
27
|
+
level: string;
|
|
28
|
+
rationale?: string | undefined;
|
|
29
|
+
}>, Record<string, string | {
|
|
30
|
+
level: string;
|
|
31
|
+
rationale?: string | undefined;
|
|
32
|
+
}>>;
|
|
33
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
34
|
+
}, "strict", z.ZodTypeAny, {
|
|
35
|
+
kind: "wr.assessment";
|
|
36
|
+
dimensions: Record<string, string | {
|
|
37
|
+
level: string;
|
|
38
|
+
rationale?: string | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
assessmentId?: string | undefined;
|
|
41
|
+
summary?: string | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
kind: "wr.assessment";
|
|
44
|
+
dimensions: Record<string, string | {
|
|
45
|
+
level: string;
|
|
46
|
+
rationale?: string | undefined;
|
|
47
|
+
}>;
|
|
48
|
+
assessmentId?: string | undefined;
|
|
49
|
+
summary?: string | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
export type AssessmentArtifactV1 = z.infer<typeof AssessmentArtifactV1Schema>;
|
|
52
|
+
export declare function isAssessmentArtifact(artifact: unknown): artifact is {
|
|
53
|
+
readonly kind: 'wr.assessment';
|
|
54
|
+
};
|
|
55
|
+
export declare function parseAssessmentArtifact(artifact: unknown): AssessmentArtifactV1 | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AssessmentArtifactV1Schema = exports.AssessmentDimensionSubmissionSchema = exports.ASSESSMENT_CONTRACT_REF = void 0;
|
|
4
|
+
exports.isAssessmentArtifact = isAssessmentArtifact;
|
|
5
|
+
exports.parseAssessmentArtifact = parseAssessmentArtifact;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const MAX_ASSESSMENT_SUMMARY_LENGTH = 1024;
|
|
8
|
+
const MAX_DIMENSION_RATIONALE_LENGTH = 512;
|
|
9
|
+
exports.ASSESSMENT_CONTRACT_REF = 'wr.contracts.assessment';
|
|
10
|
+
exports.AssessmentDimensionSubmissionSchema = zod_1.z.union([
|
|
11
|
+
zod_1.z.string().min(1),
|
|
12
|
+
zod_1.z.object({
|
|
13
|
+
level: zod_1.z.string().min(1),
|
|
14
|
+
rationale: zod_1.z.string().min(1).max(MAX_DIMENSION_RATIONALE_LENGTH).optional(),
|
|
15
|
+
}).strict(),
|
|
16
|
+
]);
|
|
17
|
+
exports.AssessmentArtifactV1Schema = zod_1.z.object({
|
|
18
|
+
kind: zod_1.z.literal('wr.assessment'),
|
|
19
|
+
assessmentId: zod_1.z.string().min(1).max(64).optional(),
|
|
20
|
+
dimensions: zod_1.z.record(exports.AssessmentDimensionSubmissionSchema).refine((value) => Object.keys(value).length > 0, 'dimensions must contain at least one entry'),
|
|
21
|
+
summary: zod_1.z.string().max(MAX_ASSESSMENT_SUMMARY_LENGTH).optional(),
|
|
22
|
+
}).strict();
|
|
23
|
+
function isAssessmentArtifact(artifact) {
|
|
24
|
+
return typeof artifact === 'object' && artifact !== null && artifact.kind === 'wr.assessment';
|
|
25
|
+
}
|
|
26
|
+
function parseAssessmentArtifact(artifact) {
|
|
27
|
+
const result = exports.AssessmentArtifactV1Schema.safeParse(artifact);
|
|
28
|
+
return result.success ? result.data : null;
|
|
29
|
+
}
|