@bastani/atomic 0.5.30 → 0.5.31-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/sdk/providers/claude.d.ts +17 -0
- package/dist/sdk/providers/claude.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts +7 -7
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +15 -15
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/sdk/providers/claude.ts +30 -3
- package/src/sdk/workflows/builtin/ralph/claude/index.ts +47 -37
- package/src/sdk/workflows/builtin/ralph/helpers/prompts.ts +30 -85
- package/src/sdk/workflows/builtin/ralph/opencode/index.ts +7 -3
|
@@ -271,6 +271,13 @@ export declare class ClaudeSessionWrapper {
|
|
|
271
271
|
* `claude` CLI binary, not the SDK — so they are silently ignored.
|
|
272
272
|
*/
|
|
273
273
|
query(prompt: string, _options?: Partial<SDKOptions>): Promise<SessionMessage[]>;
|
|
274
|
+
/**
|
|
275
|
+
* Structured output is only produced by the Agent SDK's `result` message,
|
|
276
|
+
* which interactive stages don't consume (they drive the `claude` CLI via
|
|
277
|
+
* tmux, not the SDK). Always `undefined` here — pair `outputFormat` with a
|
|
278
|
+
* headless stage to read {@link HeadlessClaudeSessionWrapper#lastStructuredOutput}.
|
|
279
|
+
*/
|
|
280
|
+
get lastStructuredOutput(): unknown;
|
|
274
281
|
/** Noop — for API symmetry with CopilotSession.disconnect(). */
|
|
275
282
|
disconnect(): Promise<void>;
|
|
276
283
|
}
|
|
@@ -328,7 +335,17 @@ export declare class HeadlessClaudeSessionWrapper {
|
|
|
328
335
|
* Claude stages run in parallel (each call gets its own SDK-assigned UUID).
|
|
329
336
|
*/
|
|
330
337
|
private _lastSessionId;
|
|
338
|
+
/**
|
|
339
|
+
* Validated structured output captured from the most recent `query()`'s
|
|
340
|
+
* `result` message. Populated only when callers pass
|
|
341
|
+
* `options.outputFormat = { type: "json_schema", schema }` and the SDK
|
|
342
|
+
* produced a `subtype: "success"` result with `structured_output` attached.
|
|
343
|
+
* Remains `undefined` on plain text runs or when the SDK fails validation
|
|
344
|
+
* (`error_max_structured_output_retries`).
|
|
345
|
+
*/
|
|
346
|
+
private _lastStructuredOutput;
|
|
331
347
|
get sessionId(): string;
|
|
348
|
+
get lastStructuredOutput(): unknown;
|
|
332
349
|
query(prompt: string | AsyncIterable<SDKUserMessage>, options?: Partial<SDKOptions>): Promise<SessionMessage[]>;
|
|
333
350
|
disconnect(): Promise<void>;
|
|
334
351
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* - `max_loops` iterations have completed (defaults to {@link DEFAULT_MAX_LOOPS}), OR
|
|
8
8
|
* - Two parallel reviewer passes both return zero findings.
|
|
9
9
|
*
|
|
10
|
-
* The reviewer stages run
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* The reviewer stages run **headless** via the Claude Agent SDK with
|
|
11
|
+
* `outputFormat: { type: "json_schema", schema: REVIEW_RESULT_JSON_SCHEMA }`,
|
|
12
|
+
* so the SDK validates {@link ReviewResultSchema} before returning. The
|
|
13
|
+
* validated object is read from `s.session.lastStructuredOutput` — no text
|
|
14
|
+
* parsing required. Running the reviewers headless (no tmux pane) keeps the
|
|
15
|
+
* graph focused on stages the user cares about and lets the SDK enforce the
|
|
16
|
+
* schema without TUI round-trips.
|
|
17
17
|
*
|
|
18
18
|
* Run: atomic workflow -n ralph -a claude "<your spec>"
|
|
19
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/claude/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/claude/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;;;;;;;;;;AA+CH,wBAuMa"}
|
|
@@ -45,7 +45,15 @@ export declare const ReviewResultSchema: z.ZodObject<{
|
|
|
45
45
|
overall_explanation: z.ZodString;
|
|
46
46
|
overall_confidence_score: z.ZodOptional<z.ZodNumber>;
|
|
47
47
|
}, z.core.$strip>;
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* JSON Schema derived from the Zod schema — used by Claude and OpenCode SDKs.
|
|
50
|
+
*
|
|
51
|
+
* `target: "openapi-3.0"` drops the `$schema` draft URL that Zod stamps
|
|
52
|
+
* by default. The Claude Agent SDK's validator silently drops
|
|
53
|
+
* `structured_output` when that metadata field is present, so we emit
|
|
54
|
+
* the OpenAPI-flavoured variant which matches the hand-written shape in
|
|
55
|
+
* the SDK's structured-output guide.
|
|
56
|
+
*/
|
|
49
57
|
export declare const REVIEW_RESULT_JSON_SCHEMA: z.core.ZodStandardJSONSchemaPayload<z.ZodObject<{
|
|
50
58
|
findings: z.ZodArray<z.ZodObject<{
|
|
51
59
|
title: z.ZodString;
|
|
@@ -74,10 +82,12 @@ export interface StructuredReviewResult {
|
|
|
74
82
|
/**
|
|
75
83
|
* Merge two parallel reviewer results into one.
|
|
76
84
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
85
|
+
* Each SDK enforces {@link ReviewResultSchema} at the provider level (Claude
|
|
86
|
+
* `outputFormat`, OpenCode `format: json_schema`, Copilot `defineTool`), so a
|
|
87
|
+
* non-null `structured` is already a validated {@link ReviewResult}. When
|
|
88
|
+
* either reviewer failed to produce validated output we propagate `null` —
|
|
89
|
+
* {@link hasActionableFindings} then treats the raw response as actionable so
|
|
90
|
+
* the loop keeps iterating instead of silently exiting on a missing reviewer.
|
|
81
91
|
*/
|
|
82
92
|
export declare function mergeReviewResults(a: StructuredReviewResult, b: StructuredReviewResult): StructuredReviewResult;
|
|
83
93
|
export interface PlannerContext {
|
|
@@ -215,16 +225,6 @@ export interface DebuggerContext {
|
|
|
215
225
|
* planner consumes.
|
|
216
226
|
*/
|
|
217
227
|
export declare function buildDebuggerReportPrompt(review: ReviewResult | null, rawReview: string, context: DebuggerContext): string;
|
|
218
|
-
/**
|
|
219
|
-
* Parse the reviewer's JSON output. Tries, in order:
|
|
220
|
-
* 1. Direct JSON.parse on the entire content.
|
|
221
|
-
* 2. The LAST fenced ```json (or unlabelled) code block.
|
|
222
|
-
* 3. The LAST balanced object containing a "findings" key in surrounding prose.
|
|
223
|
-
*
|
|
224
|
-
* Filters out P3 (minor/style) findings — only P0/P1/P2 count as actionable.
|
|
225
|
-
* Returns null when no parse strategy succeeds.
|
|
226
|
-
*/
|
|
227
|
-
export declare function parseReviewResult(content: string): ReviewResult | null;
|
|
228
228
|
export declare function filterActionable(parsed: {
|
|
229
229
|
findings: ReviewFinding[];
|
|
230
230
|
overall_correctness: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/helpers/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,8CAA8C;AAC9C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAsC9B,CAAC;AAEH,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;iBAgB7B,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/helpers/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,8CAA8C;AAC9C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAsC9B,CAAC;AAEH,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;iBAgB7B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;kBAEpC,CAAC;AAEH,mEAAmE;AACnE,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IAChC,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,sBAAsB,EACzB,CAAC,EAAE,sBAAsB,GACxB,sBAAsB,CAoCxB;AAMD,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAiC,GACzC,MAAM,CA2GR;AAKD,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,MAAM,CAwIR;AAMD,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,qBAAqB,CAwGlE;AAMD,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;KAC5C,CAAC;CACH;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GACrB,MAAM,CAgMR;AAMD,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,GACvB,MAAM,CAoHR;AAMD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,GAAG,YAAY,CAUf;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAS5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/opencode/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/sdk/workflows/builtin/ralph/opencode/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;AAyDH,wBAmMa"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.31-0",
|
|
4
4
|
"description": "Configuration management CLI and SDK for coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"typecheck": "bunx tsc --noEmit && bunx tsc -p tests --noEmit",
|
|
62
62
|
"lint": "oxlint --config=oxlint.json src",
|
|
63
63
|
"lint:fix": "oxlint --config=oxlint.json --fix src",
|
|
64
|
-
"prepare": "prek install
|
|
64
|
+
"prepare": "prek install || true"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@j178/prek": "^0.3.10",
|
|
@@ -1064,6 +1064,16 @@ export class ClaudeSessionWrapper {
|
|
|
1064
1064
|
});
|
|
1065
1065
|
}
|
|
1066
1066
|
|
|
1067
|
+
/**
|
|
1068
|
+
* Structured output is only produced by the Agent SDK's `result` message,
|
|
1069
|
+
* which interactive stages don't consume (they drive the `claude` CLI via
|
|
1070
|
+
* tmux, not the SDK). Always `undefined` here — pair `outputFormat` with a
|
|
1071
|
+
* headless stage to read {@link HeadlessClaudeSessionWrapper#lastStructuredOutput}.
|
|
1072
|
+
*/
|
|
1073
|
+
get lastStructuredOutput(): unknown {
|
|
1074
|
+
return undefined;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1067
1077
|
/** Noop — for API symmetry with CopilotSession.disconnect(). */
|
|
1068
1078
|
async disconnect(): Promise<void> {}
|
|
1069
1079
|
}
|
|
@@ -1143,11 +1153,24 @@ export class HeadlessClaudeSessionWrapper {
|
|
|
1143
1153
|
* Claude stages run in parallel (each call gets its own SDK-assigned UUID).
|
|
1144
1154
|
*/
|
|
1145
1155
|
private _lastSessionId: string = "";
|
|
1156
|
+
/**
|
|
1157
|
+
* Validated structured output captured from the most recent `query()`'s
|
|
1158
|
+
* `result` message. Populated only when callers pass
|
|
1159
|
+
* `options.outputFormat = { type: "json_schema", schema }` and the SDK
|
|
1160
|
+
* produced a `subtype: "success"` result with `structured_output` attached.
|
|
1161
|
+
* Remains `undefined` on plain text runs or when the SDK fails validation
|
|
1162
|
+
* (`error_max_structured_output_retries`).
|
|
1163
|
+
*/
|
|
1164
|
+
private _lastStructuredOutput: unknown = undefined;
|
|
1146
1165
|
|
|
1147
1166
|
get sessionId(): string {
|
|
1148
1167
|
return this._lastSessionId;
|
|
1149
1168
|
}
|
|
1150
1169
|
|
|
1170
|
+
get lastStructuredOutput(): unknown {
|
|
1171
|
+
return this._lastStructuredOutput;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1151
1174
|
async query(
|
|
1152
1175
|
prompt: string | AsyncIterable<SDKUserMessage>,
|
|
1153
1176
|
options?: Partial<SDKOptions>,
|
|
@@ -1166,12 +1189,15 @@ export class HeadlessClaudeSessionWrapper {
|
|
|
1166
1189
|
};
|
|
1167
1190
|
|
|
1168
1191
|
let sdkSessionId = "";
|
|
1192
|
+
let structuredOutput: unknown = undefined;
|
|
1169
1193
|
try {
|
|
1170
1194
|
for await (const msg of sdkQuery({ prompt, options: headlessSdkOpts })) {
|
|
1171
1195
|
if (msg.type === "result") {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
)
|
|
1196
|
+
const record = msg as Record<string, unknown>;
|
|
1197
|
+
sdkSessionId = String(record.session_id ?? "");
|
|
1198
|
+
if (record.subtype === "success" && "structured_output" in record) {
|
|
1199
|
+
structuredOutput = record.structured_output;
|
|
1200
|
+
}
|
|
1175
1201
|
}
|
|
1176
1202
|
}
|
|
1177
1203
|
} catch (err) {
|
|
@@ -1187,6 +1213,7 @@ export class HeadlessClaudeSessionWrapper {
|
|
|
1187
1213
|
);
|
|
1188
1214
|
}
|
|
1189
1215
|
this._lastSessionId = sdkSessionId;
|
|
1216
|
+
this._lastStructuredOutput = structuredOutput;
|
|
1190
1217
|
return getSessionMessages(sdkSessionId, { dir: process.cwd() });
|
|
1191
1218
|
}
|
|
1192
1219
|
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* - `max_loops` iterations have completed (defaults to {@link DEFAULT_MAX_LOOPS}), OR
|
|
8
8
|
* - Two parallel reviewer passes both return zero findings.
|
|
9
9
|
*
|
|
10
|
-
* The reviewer stages run
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* The reviewer stages run **headless** via the Claude Agent SDK with
|
|
11
|
+
* `outputFormat: { type: "json_schema", schema: REVIEW_RESULT_JSON_SCHEMA }`,
|
|
12
|
+
* so the SDK validates {@link ReviewResultSchema} before returning. The
|
|
13
|
+
* validated object is read from `s.session.lastStructuredOutput` — no text
|
|
14
|
+
* parsing required. Running the reviewers headless (no tmux pane) keeps the
|
|
15
|
+
* graph focused on stages the user cares about and lets the SDK enforce the
|
|
16
|
+
* schema without TUI round-trips.
|
|
17
17
|
*
|
|
18
18
|
* Run: atomic workflow -n ralph -a claude "<your spec>"
|
|
19
19
|
*/
|
|
@@ -27,8 +27,10 @@ import {
|
|
|
27
27
|
buildReviewPrompt,
|
|
28
28
|
buildDebuggerReportPrompt,
|
|
29
29
|
extractMarkdownBlock,
|
|
30
|
-
|
|
30
|
+
filterActionable,
|
|
31
31
|
mergeReviewResults,
|
|
32
|
+
REVIEW_RESULT_JSON_SCHEMA,
|
|
33
|
+
type ReviewResult,
|
|
32
34
|
type StructuredReviewResult,
|
|
33
35
|
} from "../helpers/prompts.ts";
|
|
34
36
|
import { hasActionableFindings } from "../helpers/review.ts";
|
|
@@ -42,13 +44,23 @@ const DEFAULT_MAX_LOOPS = 10;
|
|
|
42
44
|
// timeout is needed.
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* {@link
|
|
47
|
+
* Turn the SDK's validated structured_output (plus raw transcript text) into a
|
|
48
|
+
* {@link StructuredReviewResult}. When the SDK failed to validate the schema
|
|
49
|
+
* (`error_max_structured_output_retries`) `structured_output` is absent and
|
|
50
|
+
* we propagate `null` so {@link mergeReviewResults} treats the pass as
|
|
51
|
+
* unknown/actionable.
|
|
49
52
|
*/
|
|
50
|
-
function extractReview(
|
|
51
|
-
|
|
53
|
+
function extractReview(
|
|
54
|
+
structuredOutput: unknown,
|
|
55
|
+
rawText: string,
|
|
56
|
+
): StructuredReviewResult {
|
|
57
|
+
if (structuredOutput && typeof structuredOutput === "object") {
|
|
58
|
+
return {
|
|
59
|
+
structured: filterActionable(structuredOutput as ReviewResult),
|
|
60
|
+
raw: rawText,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { structured: null, raw: rawText };
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
export default defineWorkflow({
|
|
@@ -179,41 +191,39 @@ export default defineWorkflow({
|
|
|
179
191
|
patternResult.result,
|
|
180
192
|
].join("\n\n---\n\n");
|
|
181
193
|
|
|
182
|
-
// ── Review (two parallel passes)
|
|
194
|
+
// ── Review (two parallel headless passes with schema enforcement) ──
|
|
183
195
|
const reviewPrompt = buildReviewPrompt(prompt, {
|
|
184
196
|
changeset,
|
|
185
197
|
iteration,
|
|
186
198
|
discoveryContext,
|
|
187
199
|
});
|
|
188
200
|
|
|
189
|
-
const
|
|
190
|
-
"--agent",
|
|
191
|
-
"reviewer",
|
|
192
|
-
"--allow-dangerously-skip-permissions",
|
|
193
|
-
"--dangerously-skip-permissions",
|
|
194
|
-
];
|
|
195
|
-
|
|
196
|
-
const [reviewA, reviewB] = await Promise.all([
|
|
201
|
+
const runReviewer = (name: string) =>
|
|
197
202
|
ctx.stage(
|
|
198
|
-
{ name:
|
|
199
|
-
{ chatFlags: reviewerChatFlags },
|
|
203
|
+
{ name, headless: true },
|
|
200
204
|
{},
|
|
201
|
-
async (s) => {
|
|
202
|
-
const result = await s.session.query(reviewPrompt);
|
|
203
|
-
s.save(s.sessionId);
|
|
204
|
-
return extractReview(extractAssistantText(result, 0));
|
|
205
|
-
},
|
|
206
|
-
),
|
|
207
|
-
ctx.stage(
|
|
208
|
-
{ name: `reviewer-${iteration}-b` },
|
|
209
|
-
{ chatFlags: reviewerChatFlags },
|
|
210
205
|
{},
|
|
211
206
|
async (s) => {
|
|
212
|
-
const result = await s.session.query(reviewPrompt
|
|
207
|
+
const result = await s.session.query(reviewPrompt, {
|
|
208
|
+
agent: "reviewer",
|
|
209
|
+
permissionMode: "bypassPermissions",
|
|
210
|
+
allowDangerouslySkipPermissions: true,
|
|
211
|
+
outputFormat: {
|
|
212
|
+
type: "json_schema",
|
|
213
|
+
schema: REVIEW_RESULT_JSON_SCHEMA,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
213
216
|
s.save(s.sessionId);
|
|
214
|
-
return extractReview(
|
|
217
|
+
return extractReview(
|
|
218
|
+
s.session.lastStructuredOutput,
|
|
219
|
+
extractAssistantText(result, 0),
|
|
220
|
+
);
|
|
215
221
|
},
|
|
216
|
-
)
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const [reviewA, reviewB] = await Promise.all([
|
|
225
|
+
runReviewer(`reviewer-${iteration}-a`),
|
|
226
|
+
runReviewer(`reviewer-${iteration}-b`),
|
|
217
227
|
]);
|
|
218
228
|
|
|
219
229
|
const merged = mergeReviewResults(reviewA.result, reviewB.result);
|
|
@@ -78,8 +78,18 @@ export const ReviewResultSchema = z.object({
|
|
|
78
78
|
.describe("Overall confidence in the review (0.0–1.0)"),
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
/**
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* JSON Schema derived from the Zod schema — used by Claude and OpenCode SDKs.
|
|
83
|
+
*
|
|
84
|
+
* `target: "openapi-3.0"` drops the `$schema` draft URL that Zod stamps
|
|
85
|
+
* by default. The Claude Agent SDK's validator silently drops
|
|
86
|
+
* `structured_output` when that metadata field is present, so we emit
|
|
87
|
+
* the OpenAPI-flavoured variant which matches the hand-written shape in
|
|
88
|
+
* the SDK's structured-output guide.
|
|
89
|
+
*/
|
|
90
|
+
export const REVIEW_RESULT_JSON_SCHEMA = z.toJSONSchema(ReviewResultSchema, {
|
|
91
|
+
target: "openapi-3.0",
|
|
92
|
+
});
|
|
83
93
|
|
|
84
94
|
/** Result from a reviewer stage with structured output support. */
|
|
85
95
|
export interface StructuredReviewResult {
|
|
@@ -92,10 +102,12 @@ export interface StructuredReviewResult {
|
|
|
92
102
|
/**
|
|
93
103
|
* Merge two parallel reviewer results into one.
|
|
94
104
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
105
|
+
* Each SDK enforces {@link ReviewResultSchema} at the provider level (Claude
|
|
106
|
+
* `outputFormat`, OpenCode `format: json_schema`, Copilot `defineTool`), so a
|
|
107
|
+
* non-null `structured` is already a validated {@link ReviewResult}. When
|
|
108
|
+
* either reviewer failed to produce validated output we propagate `null` —
|
|
109
|
+
* {@link hasActionableFindings} then treats the raw response as actionable so
|
|
110
|
+
* the loop keeps iterating instead of silently exiting on a missing reviewer.
|
|
99
111
|
*/
|
|
100
112
|
export function mergeReviewResults(
|
|
101
113
|
a: StructuredReviewResult,
|
|
@@ -103,38 +115,30 @@ export function mergeReviewResults(
|
|
|
103
115
|
): StructuredReviewResult {
|
|
104
116
|
const rawCombined = [a.raw, b.raw].filter(Boolean).join("\n\n---\n\n");
|
|
105
117
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
b.structured ?? (b.raw.trim() ? parseReviewResult(b.raw) : null);
|
|
111
|
-
|
|
112
|
-
if (!parsedA && !parsedB) {
|
|
118
|
+
// Conservative: any missing structured output → propagate null. Fabricating
|
|
119
|
+
// a "patch is correct" default here is how the loop previously exited after
|
|
120
|
+
// a single iteration when one reviewer's output failed SDK validation.
|
|
121
|
+
if (!a.structured || !b.structured) {
|
|
113
122
|
return { structured: null, raw: rawCombined };
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
const findingsA = parsedA?.findings ?? [];
|
|
117
|
-
const findingsB = parsedB?.findings ?? [];
|
|
118
|
-
|
|
119
|
-
const correctnessA = parsedA?.overall_correctness ?? "patch is correct";
|
|
120
|
-
const correctnessB = parsedB?.overall_correctness ?? "patch is correct";
|
|
121
125
|
const isIncorrect =
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
a.structured.overall_correctness === "patch is incorrect" ||
|
|
127
|
+
b.structured.overall_correctness === "patch is incorrect";
|
|
124
128
|
|
|
125
129
|
const explanations = [
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
].filter(
|
|
130
|
+
a.structured.overall_explanation,
|
|
131
|
+
b.structured.overall_explanation,
|
|
132
|
+
].filter((e): e is string => typeof e === "string" && e.length > 0);
|
|
129
133
|
|
|
130
134
|
const confidences = [
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
a.structured.overall_confidence_score,
|
|
136
|
+
b.structured.overall_confidence_score,
|
|
133
137
|
].filter((c): c is number => c !== undefined);
|
|
134
138
|
|
|
135
139
|
return {
|
|
136
140
|
structured: {
|
|
137
|
-
findings: [...
|
|
141
|
+
findings: [...a.structured.findings, ...b.structured.findings],
|
|
138
142
|
overall_correctness: isIncorrect
|
|
139
143
|
? "patch is incorrect"
|
|
140
144
|
: "patch is correct",
|
|
@@ -981,65 +985,6 @@ the "Pitfalls" section entirely if there are none. Begin now.`;
|
|
|
981
985
|
// PARSING HELPERS
|
|
982
986
|
// ============================================================================
|
|
983
987
|
|
|
984
|
-
/**
|
|
985
|
-
* Parse the reviewer's JSON output. Tries, in order:
|
|
986
|
-
* 1. Direct JSON.parse on the entire content.
|
|
987
|
-
* 2. The LAST fenced ```json (or unlabelled) code block.
|
|
988
|
-
* 3. The LAST balanced object containing a "findings" key in surrounding prose.
|
|
989
|
-
*
|
|
990
|
-
* Filters out P3 (minor/style) findings — only P0/P1/P2 count as actionable.
|
|
991
|
-
* Returns null when no parse strategy succeeds.
|
|
992
|
-
*/
|
|
993
|
-
export function parseReviewResult(content: string): ReviewResult | null {
|
|
994
|
-
// Strategy 1: direct JSON
|
|
995
|
-
try {
|
|
996
|
-
const parsed = JSON.parse(content);
|
|
997
|
-
if (parsed && parsed.findings && parsed.overall_correctness) {
|
|
998
|
-
return filterActionable(parsed);
|
|
999
|
-
}
|
|
1000
|
-
} catch {
|
|
1001
|
-
/* fall through */
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
// Strategy 2: last fenced code block
|
|
1005
|
-
const blockRe = /```(?:json)?\s*\n([\s\S]*?)\n```/g;
|
|
1006
|
-
let lastBlock: string | null = null;
|
|
1007
|
-
let blockMatch: RegExpExecArray | null;
|
|
1008
|
-
while ((blockMatch = blockRe.exec(content)) !== null) {
|
|
1009
|
-
if (blockMatch[1]) lastBlock = blockMatch[1];
|
|
1010
|
-
}
|
|
1011
|
-
if (lastBlock !== null) {
|
|
1012
|
-
try {
|
|
1013
|
-
const parsed = JSON.parse(lastBlock);
|
|
1014
|
-
if (parsed && parsed.findings && parsed.overall_correctness) {
|
|
1015
|
-
return filterActionable(parsed);
|
|
1016
|
-
}
|
|
1017
|
-
} catch {
|
|
1018
|
-
/* fall through */
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
// Strategy 3: last "{...findings...}" object in surrounding prose
|
|
1023
|
-
const objRe = /\{[\s\S]*?"findings"[\s\S]*?\}/g;
|
|
1024
|
-
let lastObj: string | null = null;
|
|
1025
|
-
let objMatch: RegExpExecArray | null;
|
|
1026
|
-
while ((objMatch = objRe.exec(content)) !== null) {
|
|
1027
|
-
lastObj = objMatch[0];
|
|
1028
|
-
}
|
|
1029
|
-
if (lastObj !== null) {
|
|
1030
|
-
try {
|
|
1031
|
-
const parsed = JSON.parse(lastObj);
|
|
1032
|
-
if (parsed && parsed.findings && parsed.overall_correctness) {
|
|
1033
|
-
return filterActionable(parsed);
|
|
1034
|
-
}
|
|
1035
|
-
} catch {
|
|
1036
|
-
/* nothing more to try */
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
return null;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
988
|
export function filterActionable(parsed: {
|
|
1044
989
|
findings: ReviewFinding[];
|
|
1045
990
|
overall_correctness: string;
|
|
@@ -46,15 +46,19 @@ function extractResponseText(
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Extract a {@link StructuredReviewResult} from an OpenCode prompt response.
|
|
49
|
-
*
|
|
49
|
+
*
|
|
50
|
+
* The OpenCode SDK places the SDK-validated structured output on the
|
|
51
|
+
* AssistantMessage as `structured` (see `@opencode-ai/sdk` v2 types.gen.d.ts
|
|
52
|
+
* — AssistantMessage.structured). Returns `structured: null` whenever the
|
|
53
|
+
* SDK did not produce a validated object so {@link mergeReviewResults}
|
|
54
|
+
* treats the pass as unknown/actionable.
|
|
50
55
|
*/
|
|
51
56
|
function extractReview(
|
|
52
57
|
data: { info?: Record<string, unknown>; parts: Array<{ type: string; [key: string]: unknown }> },
|
|
53
58
|
): StructuredReviewResult {
|
|
54
59
|
const raw = extractResponseText(data.parts);
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
const structuredOutput = data.info?.structured_output;
|
|
61
|
+
const structuredOutput = data.info?.structured;
|
|
58
62
|
if (structuredOutput && typeof structuredOutput === "object") {
|
|
59
63
|
return {
|
|
60
64
|
structured: filterActionable(structuredOutput as ReviewResult),
|