@nt-ai-lab/deterministic-agent-workflow-cli 0.3.6 → 0.4.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/features/workflow-runner/entrypoint/review-routes.d.ts +1 -1
- package/dist/features/workflow-runner/entrypoint/review-routes.js +25 -51
- package/dist/features/workflow-runner/entrypoint/review-routes.spec.js +10 -10
- package/dist/features/workflow-runner/entrypoint/workflow-runner.js +1 -1
- package/package.json +9 -4
|
@@ -3,4 +3,4 @@ import type { RunnerResult } from '../../../platform/domain/workflow-runner-type
|
|
|
3
3
|
/** @riviere-role cli-entrypoint */
|
|
4
4
|
export declare function handleRecordReviewRoute<TWorkflow extends RehydratableWorkflow<TState>, TState extends BaseWorkflowState<TStateName>, TDeps, TStateName extends string, TOperation extends string>(engine: {
|
|
5
5
|
readonly hasSessionStarted: (sessionId: string) => boolean;
|
|
6
|
-
}, engineDeps: WorkflowEngineDeps, workflowDefinition: WorkflowDefinition<TWorkflow, TState, TDeps, TStateName, TOperation>, args: readonly string[],
|
|
6
|
+
}, engineDeps: WorkflowEngineDeps, workflowDefinition: WorkflowDefinition<TWorkflow, TState, TDeps, TStateName, TOperation>, args: readonly string[], getSessionId: (() => string) | undefined): RunnerResult;
|
|
@@ -18,70 +18,44 @@ function blockedResult(output) {
|
|
|
18
18
|
exitCode: EXIT_BLOCK,
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
ok: false,
|
|
36
|
-
message: `Missing value for flag: ${key}`,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
flags.set(key, value);
|
|
21
|
+
function parseReviewArguments(args) {
|
|
22
|
+
const reviewType = args[1];
|
|
23
|
+
const reviewJson = args[2];
|
|
24
|
+
if (args.length !== 3 || typeof reviewType !== 'string' || typeof reviewJson !== 'string') {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
message: 'record-review requires <review-type> and <review-json> arguments',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (reviewType.length === 0) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
message: 'record-review requires a non-empty review type',
|
|
34
|
+
};
|
|
40
35
|
}
|
|
41
36
|
return {
|
|
42
37
|
ok: true,
|
|
43
|
-
|
|
38
|
+
reviewType,
|
|
39
|
+
reviewJson,
|
|
44
40
|
};
|
|
45
41
|
}
|
|
46
|
-
function validateReviewFlags(flags) {
|
|
47
|
-
for (const key of flags.keys()) {
|
|
48
|
-
if (key !== '--type') {
|
|
49
|
-
return `Unknown flag: ${key}`;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (!flags.has('--type')) {
|
|
53
|
-
return 'record-review requires --type';
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
42
|
function isReviewAllowed(allowedWorkflowOperations) {
|
|
58
43
|
return allowedWorkflowOperations.some((allowedWorkflowOperation) => allowedWorkflowOperation === 'record-review');
|
|
59
44
|
}
|
|
60
45
|
/** @riviere-role cli-entrypoint */
|
|
61
|
-
export function handleRecordReviewRoute(engine, engineDeps, workflowDefinition, args,
|
|
46
|
+
export function handleRecordReviewRoute(engine, engineDeps, workflowDefinition, args, getSessionId) {
|
|
62
47
|
if (getSessionId === undefined) {
|
|
63
48
|
return errorResult('record-review requires an active workflow session');
|
|
64
49
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const parsedFlags = parseFlagArgs(args.slice(1));
|
|
69
|
-
if (!parsedFlags.ok) {
|
|
70
|
-
return errorResult(parsedFlags.message);
|
|
71
|
-
}
|
|
72
|
-
const flagError = validateReviewFlags(parsedFlags.flags);
|
|
73
|
-
if (flagError !== null) {
|
|
74
|
-
return errorResult(flagError);
|
|
75
|
-
}
|
|
76
|
-
const reviewType = parsedFlags.flags.get('--type');
|
|
77
|
-
if (reviewType === undefined) {
|
|
78
|
-
return errorResult('record-review requires a non-empty --type value');
|
|
50
|
+
const parsedArguments = parseReviewArguments(args);
|
|
51
|
+
if (!parsedArguments.ok) {
|
|
52
|
+
return errorResult(parsedArguments.message);
|
|
79
53
|
}
|
|
80
54
|
const sessionId = getSessionId();
|
|
81
55
|
if (!engine.hasSessionStarted(sessionId)) {
|
|
82
56
|
return errorResult(`Session ${sessionId} has not been started`);
|
|
83
57
|
}
|
|
84
|
-
const reviewPayloadResult = parseReviewPayload(
|
|
58
|
+
const reviewPayloadResult = parseReviewPayload(parsedArguments.reviewJson);
|
|
85
59
|
if (!reviewPayloadResult.ok) {
|
|
86
60
|
return errorResult(reviewPayloadResult.message);
|
|
87
61
|
}
|
|
@@ -99,7 +73,7 @@ export function handleRecordReviewRoute(engine, engineDeps, workflowDefinition,
|
|
|
99
73
|
}
|
|
100
74
|
const createdAt = engineDeps.now();
|
|
101
75
|
const stored = engineDeps.store.recordReviewWithEvent(sessionId, createdAt, {
|
|
102
|
-
reviewType,
|
|
76
|
+
reviewType: parsedArguments.reviewType,
|
|
103
77
|
sourceState: currentStateName,
|
|
104
78
|
...parsedReview.data,
|
|
105
79
|
}, currentStateName);
|
|
@@ -108,7 +82,7 @@ export function handleRecordReviewRoute(engine, engineDeps, workflowDefinition,
|
|
|
108
82
|
id: stored.id,
|
|
109
83
|
sessionId,
|
|
110
84
|
createdAt: stored.createdAt,
|
|
111
|
-
reviewType,
|
|
85
|
+
reviewType: parsedArguments.reviewType,
|
|
112
86
|
verdict: parsedReview.data.verdict,
|
|
113
87
|
});
|
|
114
88
|
}
|
|
@@ -116,11 +90,11 @@ function computeCurrentState(workflowDefinition, storedEvents) {
|
|
|
116
90
|
const state = reduceWorkflowStateFromStoredEvents(workflowDefinition, storedEvents);
|
|
117
91
|
return state.currentStateMachineState;
|
|
118
92
|
}
|
|
119
|
-
function parseReviewPayload(
|
|
93
|
+
function parseReviewPayload(reviewJson) {
|
|
120
94
|
try {
|
|
121
95
|
return {
|
|
122
96
|
ok: true,
|
|
123
|
-
payload: JSON.parse(
|
|
97
|
+
payload: JSON.parse(reviewJson),
|
|
124
98
|
};
|
|
125
99
|
}
|
|
126
100
|
catch (error) {
|
|
@@ -170,20 +170,20 @@ function createEngineDeps(store) {
|
|
|
170
170
|
transcriptReader: { readMessages: () => [] },
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
|
-
function recordReview(store,
|
|
174
|
-
return handleRecordReviewRoute({ hasSessionStarted: (candidateSessionId) => store.hasSessionStarted(candidateSessionId) }, createEngineDeps(store), workflowDefinition, ['record-review', '
|
|
173
|
+
function recordReview(store, reviewJson, sessionId = 'test-session') {
|
|
174
|
+
return handleRecordReviewRoute({ hasSessionStarted: (candidateSessionId) => store.hasSessionStarted(candidateSessionId) }, createEngineDeps(store), workflowDefinition, ['record-review', 'custom-review', reviewJson], () => sessionId);
|
|
175
175
|
}
|
|
176
176
|
describe('handleRecordReviewRoute', () => {
|
|
177
|
-
it('returns error when
|
|
177
|
+
it('returns error when review JSON argument is invalid JSON', () => {
|
|
178
178
|
const store = new ReviewRouteStore();
|
|
179
|
-
const result = recordReview(store,
|
|
179
|
+
const result = recordReview(store, '{');
|
|
180
180
|
expect(result.exitCode).toBe(EXIT_ERROR);
|
|
181
181
|
expect(result.output).toContain('Invalid review JSON');
|
|
182
182
|
expect(store.storedReviews).toStrictEqual([]);
|
|
183
183
|
});
|
|
184
184
|
it('returns error when verdict is unknown', () => {
|
|
185
185
|
const store = new ReviewRouteStore();
|
|
186
|
-
const result = recordReview(store,
|
|
186
|
+
const result = recordReview(store, JSON.stringify({
|
|
187
187
|
verdict: 'MAYBE',
|
|
188
188
|
findings: []
|
|
189
189
|
}));
|
|
@@ -193,7 +193,7 @@ describe('handleRecordReviewRoute', () => {
|
|
|
193
193
|
});
|
|
194
194
|
it('returns error when session has not started', () => {
|
|
195
195
|
const store = new ReviewRouteStore();
|
|
196
|
-
const result = recordReview(store,
|
|
196
|
+
const result = recordReview(store, JSON.stringify({
|
|
197
197
|
verdict: 'PASS',
|
|
198
198
|
findings: []
|
|
199
199
|
}), 'missing-session');
|
|
@@ -204,7 +204,7 @@ describe('handleRecordReviewRoute', () => {
|
|
|
204
204
|
});
|
|
205
205
|
it('records review row and review event when payload is valid', () => {
|
|
206
206
|
const store = new ReviewRouteStore();
|
|
207
|
-
const result = recordReview(store,
|
|
207
|
+
const result = recordReview(store, JSON.stringify({
|
|
208
208
|
verdict: 'FAIL',
|
|
209
209
|
findings: [{ title: 'Missing command tests' }],
|
|
210
210
|
}));
|
|
@@ -234,11 +234,11 @@ describe('handleRecordReviewRoute', () => {
|
|
|
234
234
|
});
|
|
235
235
|
it('preserves repeated review attempts', () => {
|
|
236
236
|
const store = new ReviewRouteStore();
|
|
237
|
-
recordReview(store,
|
|
237
|
+
recordReview(store, JSON.stringify({
|
|
238
238
|
verdict: 'FAIL',
|
|
239
239
|
findings: [{ title: 'First failure' }]
|
|
240
240
|
}));
|
|
241
|
-
recordReview(store,
|
|
241
|
+
recordReview(store, JSON.stringify({
|
|
242
242
|
verdict: 'PASS',
|
|
243
243
|
findings: []
|
|
244
244
|
}));
|
|
@@ -259,7 +259,7 @@ describe('handleRecordReviewRoute', () => {
|
|
|
259
259
|
});
|
|
260
260
|
it('returns blocked when current state disallows record-review', () => {
|
|
261
261
|
const store = new ReviewRouteStore('PLANNING');
|
|
262
|
-
const result = recordReview(store,
|
|
262
|
+
const result = recordReview(store, JSON.stringify({
|
|
263
263
|
verdict: 'PASS',
|
|
264
264
|
findings: []
|
|
265
265
|
}));
|
|
@@ -166,7 +166,7 @@ function resolveBuiltinRoute(engine, engineDeps, config, args, routeName, readSt
|
|
|
166
166
|
case 'record-reflection':
|
|
167
167
|
return handleRecordReflectionRoute(engine, engineDeps, args, readStdin, getSessionId);
|
|
168
168
|
case 'record-review':
|
|
169
|
-
return handleRecordReviewRoute(engine, engineDeps, config.workflowDefinition, args,
|
|
169
|
+
return handleRecordReviewRoute(engine, engineDeps, config.workflowDefinition, args, getSessionId);
|
|
170
170
|
case 'write-journal':
|
|
171
171
|
return handleWriteJournalRoute(engine, args, getSessionId);
|
|
172
172
|
default:
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nt-ai-lab/deterministic-agent-workflow-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/NTCoding/deterministic-agent-workflows.git",
|
|
9
|
+
"directory": "packages/deterministic-agent-workflows-cli"
|
|
10
|
+
},
|
|
6
11
|
"exports": {
|
|
7
12
|
".": "./dist/index.js"
|
|
8
13
|
},
|
|
@@ -12,9 +17,9 @@
|
|
|
12
17
|
],
|
|
13
18
|
"dependencies": {
|
|
14
19
|
"zod": "^3.25.76",
|
|
15
|
-
"@nt-ai-lab/deterministic-agent-workflow-engine": "0.
|
|
16
|
-
"@nt-ai-lab/deterministic-agent-workflow-dsl": "0.
|
|
17
|
-
"@nt-ai-lab/deterministic-agent-workflow-event-store": "0.
|
|
20
|
+
"@nt-ai-lab/deterministic-agent-workflow-engine": "0.4.0",
|
|
21
|
+
"@nt-ai-lab/deterministic-agent-workflow-dsl": "0.4.0",
|
|
22
|
+
"@nt-ai-lab/deterministic-agent-workflow-event-store": "0.4.0"
|
|
18
23
|
},
|
|
19
24
|
"devDependencies": {
|
|
20
25
|
"vitest": "^2.1.9"
|