@dougharness/codex 1.0.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/LICENSE +21 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +44 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +20 -0
- package/dist/cli.js +108 -0
- package/dist/cli.js.map +1 -0
- package/dist/contract.d.ts +54 -0
- package/dist/contract.js +305 -0
- package/dist/contract.js.map +1 -0
- package/dist/git.d.ts +14 -0
- package/dist/git.js +36 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +21 -0
- package/dist/parse.js +115 -0
- package/dist/parse.js.map +1 -0
- package/dist/prompt.d.ts +14 -0
- package/dist/prompt.js +62 -0
- package/dist/prompt.js.map +1 -0
- package/dist/run.d.ts +65 -0
- package/dist/run.js +195 -0
- package/dist/run.js.map +1 -0
- package/dist/schema.d.ts +174 -0
- package/dist/schema.js +84 -0
- package/dist/schema.js.map +1 -0
- package/package.json +24 -0
package/dist/git.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Deterministic facts about the change, computed by git rather than asserted by a model.
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
function git(args, cwd) {
|
|
4
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 64 * 1024 * 1024 });
|
|
5
|
+
}
|
|
6
|
+
/** `git status --porcelain` lines, sorted, so two snapshots can be compared. Untracked files included. */
|
|
7
|
+
export function statusSnapshot(dir) {
|
|
8
|
+
return git(["status", "--porcelain", "--untracked-files=all"], dir)
|
|
9
|
+
.split("\n")
|
|
10
|
+
.map((l) => l.trimEnd())
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.sort();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The commit checked out in dir versus the commit `head` names. The reviewer runs commands in dir,
|
|
16
|
+
* so they must be the same tree; otherwise every test it runs exercises the wrong code.
|
|
17
|
+
*/
|
|
18
|
+
export function checkedOutMismatch(dir, head) {
|
|
19
|
+
const actual = git(["rev-parse", "--verify", "HEAD"], dir);
|
|
20
|
+
const wanted = git(["rev-parse", "--verify", `${head}^{commit}`], dir);
|
|
21
|
+
if (actual === wanted)
|
|
22
|
+
return null;
|
|
23
|
+
return `${head} (${wanted.slice(0, 12)}) is not what is checked out in ${dir} (${actual.slice(0, 12)}); check it out there or point --dir at its worktree`;
|
|
24
|
+
}
|
|
25
|
+
/** Throws with git's own message when a ref does not resolve or dir is not a repository. */
|
|
26
|
+
export function changeFacts(dir, base, head) {
|
|
27
|
+
const range = `${base}...${head}`;
|
|
28
|
+
const names = git(["diff", "--name-only", range], dir);
|
|
29
|
+
const diff = git(["diff", range], dir);
|
|
30
|
+
return {
|
|
31
|
+
changedFiles: names.split("\n").map((s) => s.trim()).filter(Boolean),
|
|
32
|
+
diff,
|
|
33
|
+
diffBytes: Buffer.byteLength(diff, "utf8"),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,yFAAyF;AAEzF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,SAAS,GAAG,CAAC,IAAc,EAAE,GAAW;IACtC,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AAC9H,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,uBAAuB,CAAC,EAAE,GAAG,CAAC;SAChE,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACvB,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,IAAY;IAC1D,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC;IACvE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,GAAG,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,mCAAmC,GAAG,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,sDAAsD,CAAC;AAC7J,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,IAAY,EAAE,IAAY;IACjE,MAAM,KAAK,GAAG,GAAG,IAAI,MAAM,IAAI,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACvC,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACpE,IAAI;QACJ,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;KAC3C,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { runCodexReview, codexArgs, DEFAULT_TIMEOUT_MS, DEFAULT_SANDBOX, REVIEW_MARKER } from "./run.js";
|
|
2
|
+
export { buildPrompt, MAX_EMBEDDED_DIFF_BYTES } from "./prompt.js";
|
|
3
|
+
export { parseEvents, extractJsonObject } from "./parse.js";
|
|
4
|
+
export { changeFacts, statusSnapshot } from "./git.js";
|
|
5
|
+
export { MODEL_REVIEW_SCHEMA, validateModelReview, exitCodeFor, SEVERITIES, VERDICTS, SANDBOXES } from "./schema.js";
|
|
6
|
+
export { checkReviewResult, CONTRACT_RULES, ERROR_KINDS, enforceBlockerEvidence, blockerEvidenceProblem } from "./contract.js";
|
|
7
|
+
export type { RuleId } from "./contract.js";
|
|
8
|
+
export type { ReviewResult, ReviewIssue, ReviewError, CommandRun, ModelReview, Severity, Verdict, Sandbox } from "./schema.js";
|
|
9
|
+
export type { ReviewOptions } from "./run.js";
|
|
10
|
+
export type { PromptInput } from "./prompt.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { runCodexReview, codexArgs, DEFAULT_TIMEOUT_MS, DEFAULT_SANDBOX, REVIEW_MARKER } from "./run.js";
|
|
2
|
+
export { buildPrompt, MAX_EMBEDDED_DIFF_BYTES } from "./prompt.js";
|
|
3
|
+
export { parseEvents, extractJsonObject } from "./parse.js";
|
|
4
|
+
export { changeFacts, statusSnapshot } from "./git.js";
|
|
5
|
+
export { MODEL_REVIEW_SCHEMA, validateModelReview, exitCodeFor, SEVERITIES, VERDICTS, SANDBOXES } from "./schema.js";
|
|
6
|
+
export { checkReviewResult, CONTRACT_RULES, ERROR_KINDS, enforceBlockerEvidence, blockerEvidenceProblem } from "./contract.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzG,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CommandRun } from "./schema.js";
|
|
2
|
+
export interface ParsedEvents {
|
|
3
|
+
commandsRun: CommandRun[];
|
|
4
|
+
/** Text of the last agent_message item, or null if none completed. */
|
|
5
|
+
finalMessage: string | null;
|
|
6
|
+
usage: {
|
|
7
|
+
inputTokens: number;
|
|
8
|
+
outputTokens: number;
|
|
9
|
+
} | null;
|
|
10
|
+
/** Lines that were not valid JSON (codex prints some diagnostics to stdout). */
|
|
11
|
+
unparsedLines: number;
|
|
12
|
+
threadId: string | null;
|
|
13
|
+
/** Messages from `error` and `turn.failed` events. Codex reports API errors here, not on stderr. */
|
|
14
|
+
errors: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function parseEvents(jsonl: string): ParsedEvents;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts a JSON object from a model message. Accepts bare JSON, a ```json fence, or
|
|
19
|
+
* JSON with leading/trailing prose. Returns null when nothing parses.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractJsonObject(text: string): unknown | null;
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Parses the JSONL event stream from `codex exec --json`.
|
|
2
|
+
// Evidence comes from events, not from the model's prose: a command "ran" only if a
|
|
3
|
+
// command_execution item completed, and its exit code is whatever Codex observed.
|
|
4
|
+
const TAIL_CHARS = 600;
|
|
5
|
+
export function parseEvents(jsonl) {
|
|
6
|
+
const commands = new Map();
|
|
7
|
+
let order = 0;
|
|
8
|
+
const seq = new Map();
|
|
9
|
+
let finalMessage = null;
|
|
10
|
+
let usage = null;
|
|
11
|
+
let unparsedLines = 0;
|
|
12
|
+
let threadId = null;
|
|
13
|
+
const errors = [];
|
|
14
|
+
for (const rawLine of jsonl.split("\n")) {
|
|
15
|
+
const line = rawLine.trim();
|
|
16
|
+
if (!line)
|
|
17
|
+
continue;
|
|
18
|
+
let ev;
|
|
19
|
+
try {
|
|
20
|
+
ev = JSON.parse(line);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
unparsedLines++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (!ev || typeof ev !== "object")
|
|
27
|
+
continue;
|
|
28
|
+
if (ev.type === "thread.started" && typeof ev.thread_id === "string")
|
|
29
|
+
threadId = ev.thread_id;
|
|
30
|
+
if (ev.type === "error" && typeof ev.message === "string")
|
|
31
|
+
errors.push(ev.message);
|
|
32
|
+
if (ev.type === "turn.failed" && ev.error && typeof ev.error.message === "string" && !errors.includes(ev.error.message))
|
|
33
|
+
errors.push(ev.error.message);
|
|
34
|
+
if (ev.type === "turn.completed" && ev.usage && typeof ev.usage === "object") {
|
|
35
|
+
usage = { inputTokens: Number(ev.usage.input_tokens) || 0, outputTokens: Number(ev.usage.output_tokens) || 0 };
|
|
36
|
+
}
|
|
37
|
+
const item = ev.item;
|
|
38
|
+
if (!item || typeof item !== "object")
|
|
39
|
+
continue;
|
|
40
|
+
if (item.type === "command_execution" && (ev.type === "item.completed" || ev.type === "item.started")) {
|
|
41
|
+
const c = item;
|
|
42
|
+
const key = typeof c.id === "string" ? c.id : `anon-${order}`;
|
|
43
|
+
if (!seq.has(key))
|
|
44
|
+
seq.set(key, order++);
|
|
45
|
+
const completed = ev.type === "item.completed";
|
|
46
|
+
const exitCode = completed && typeof c.exit_code === "number" ? c.exit_code : null;
|
|
47
|
+
const out = typeof c.aggregated_output === "string" ? c.aggregated_output : "";
|
|
48
|
+
commands.set(key, {
|
|
49
|
+
command: typeof c.command === "string" ? c.command : "",
|
|
50
|
+
exitCode,
|
|
51
|
+
ok: exitCode === 0,
|
|
52
|
+
outputTail: out.length > TAIL_CHARS ? out.slice(-TAIL_CHARS) : out || undefined,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (item.type === "agent_message" && ev.type === "item.completed" && typeof item.text === "string") {
|
|
56
|
+
finalMessage = item.text;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const commandsRun = [...commands.entries()].sort((a, b) => (seq.get(a[0]) ?? 0) - (seq.get(b[0]) ?? 0)).map(([, c]) => c);
|
|
60
|
+
return { commandsRun, finalMessage, usage, unparsedLines, threadId, errors };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Extracts a JSON object from a model message. Accepts bare JSON, a ```json fence, or
|
|
64
|
+
* JSON with leading/trailing prose. Returns null when nothing parses.
|
|
65
|
+
*/
|
|
66
|
+
export function extractJsonObject(text) {
|
|
67
|
+
const trimmed = text.trim();
|
|
68
|
+
const direct = tryParse(trimmed);
|
|
69
|
+
if (direct !== undefined)
|
|
70
|
+
return direct;
|
|
71
|
+
const fence = /```(?:json)?\s*\n([\s\S]*?)\n\s*```/m.exec(trimmed);
|
|
72
|
+
if (fence) {
|
|
73
|
+
const fenced = tryParse(fence[1].trim());
|
|
74
|
+
if (fenced !== undefined)
|
|
75
|
+
return fenced;
|
|
76
|
+
}
|
|
77
|
+
// Last resort: the outermost {...} span. Scan for a balanced object so trailing prose does not break it.
|
|
78
|
+
const start = trimmed.indexOf("{");
|
|
79
|
+
if (start < 0)
|
|
80
|
+
return null;
|
|
81
|
+
let depth = 0;
|
|
82
|
+
let inString = false;
|
|
83
|
+
for (let i = start; i < trimmed.length; i++) {
|
|
84
|
+
const ch = trimmed[i];
|
|
85
|
+
if (inString) {
|
|
86
|
+
if (ch === "\\")
|
|
87
|
+
i++;
|
|
88
|
+
else if (ch === '"')
|
|
89
|
+
inString = false;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (ch === '"')
|
|
93
|
+
inString = true;
|
|
94
|
+
else if (ch === "{")
|
|
95
|
+
depth++;
|
|
96
|
+
else if (ch === "}") {
|
|
97
|
+
depth--;
|
|
98
|
+
if (depth === 0) {
|
|
99
|
+
const candidate = tryParse(trimmed.slice(start, i + 1));
|
|
100
|
+
return candidate === undefined ? null : candidate;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function tryParse(s) {
|
|
107
|
+
try {
|
|
108
|
+
const v = JSON.parse(s);
|
|
109
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PromptInput {
|
|
2
|
+
spec: string;
|
|
3
|
+
base: string;
|
|
4
|
+
head: string;
|
|
5
|
+
changedFiles: string[];
|
|
6
|
+
/** Full unified diff base...head, or null if it was too large to embed. */
|
|
7
|
+
diff: string | null;
|
|
8
|
+
/** Bytes of the diff when it was not embedded. */
|
|
9
|
+
diffBytes?: number;
|
|
10
|
+
/** Commands the reviewer is encouraged to run (the task's verify command, the project's test command). */
|
|
11
|
+
verifyCommands?: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare const MAX_EMBEDDED_DIFF_BYTES = 60000;
|
|
14
|
+
export declare function buildPrompt(input: PromptInput): string;
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Builds the refutation-oriented review prompt. Pure; no I/O.
|
|
2
|
+
// The diff is embedded when small so the reviewer starts from evidence, not from a description of it.
|
|
3
|
+
export const MAX_EMBEDDED_DIFF_BYTES = 60_000;
|
|
4
|
+
export function buildPrompt(input) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
lines.push("You are an adversarial code reviewer. Your job is to REFUTE the claim that this change correctly and completely implements the spec below.");
|
|
7
|
+
lines.push("You did not write this code and you have no stake in it passing. Assume it is wrong until the evidence says otherwise.");
|
|
8
|
+
lines.push("Run tests, type checks, scripts, and git commands freely, but never edit, create, or delete files in the repository and never commit, checkout, or reset. Put scratch scripts in the system temp directory. The working tree is compared before and after your run; any change voids your review.");
|
|
9
|
+
lines.push("");
|
|
10
|
+
lines.push("## Spec the change claims to implement");
|
|
11
|
+
lines.push("");
|
|
12
|
+
lines.push(input.spec.trim());
|
|
13
|
+
lines.push("");
|
|
14
|
+
lines.push("## Change under review");
|
|
15
|
+
lines.push("");
|
|
16
|
+
lines.push(`Base ref: ${input.base}`);
|
|
17
|
+
lines.push(`Head ref: ${input.head}`);
|
|
18
|
+
lines.push(`Changed files (from git diff --name-only ${input.base}...${input.head}):`);
|
|
19
|
+
if (input.changedFiles.length === 0)
|
|
20
|
+
lines.push(" (none: the diff is empty)");
|
|
21
|
+
for (const f of input.changedFiles)
|
|
22
|
+
lines.push(` - ${f}`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
if (input.diff !== null) {
|
|
25
|
+
lines.push("Unified diff:");
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("```diff");
|
|
28
|
+
lines.push(input.diff.replace(/```/g, "` ` `"));
|
|
29
|
+
lines.push("```");
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
lines.push(`The diff is ${input.diffBytes ?? "?"} bytes, too large to embed. Read it with: git diff ${input.base}...${input.head}`);
|
|
33
|
+
}
|
|
34
|
+
lines.push("");
|
|
35
|
+
lines.push("## How to review");
|
|
36
|
+
lines.push("");
|
|
37
|
+
lines.push("1. For each requirement in the spec, find the line in the diff that satisfies it or record that nothing does.");
|
|
38
|
+
lines.push("2. Try to break it: run the tests, then write and run a quick script or test invocation that exercises an edge the spec implies (empty input, boundary values, unicode, errors). A requirement with no failing-before test is unverified, not verified.");
|
|
39
|
+
lines.push("3. Look for changes the spec did not ask for: extra files, renamed symbols, weakened or skipped tests, changed defaults, formatting churn.");
|
|
40
|
+
lines.push("4. Check for the classic false pass: tests that assert nothing, `skip`/`only`, a command that succeeded because it ran zero tests.");
|
|
41
|
+
if (input.verifyCommands && input.verifyCommands.length) {
|
|
42
|
+
lines.push(`5. Run these commands and treat a non-zero exit as a blocker: ${input.verifyCommands.map((c) => `\`${c}\``).join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push("");
|
|
45
|
+
lines.push("## What counts as a blocker");
|
|
46
|
+
lines.push("");
|
|
47
|
+
lines.push("A blocker must demonstrate either a failure of a spec sentence or acceptance criterion, which you quote, or a violation of a repository invariant: safety, security, data integrity, public compatibility, or required verification (a verify or acceptance command that exits non-zero). It must be caused by this diff, on a supported or reasonably foreseeable input, and carry a reproduction: a command you ran (an entry in commandsRun) whose exit code or quoted output shows the failure; a finding from static inspection alone, with no command that demonstrates it, is major at most. A test-coverage gap against the spec (a requirement without a test, an assertion missing) is major and never a blocker: the reviewer owns spec compliance, so say so in the issue. An input the spec names as unsupported, or a pathological input no supported caller produces, is at most minor. This is not a downgrade of destructive behavior: a change that deletes, overwrites, or corrupts data the spec did not name is a blocker under data integrity even when no spec sentence forbids it. A `fail` verdict requires at least one `blocker` issue: an issue that is only `major` or `minor` is reported with a `pass` verdict, and a review whose findings are all advisory passes with notes.");
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push("## Verdict rules");
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push("- `fail` only when at least one issue is a `blocker`: a requirement unmet, a test or verify command failing, or behavior the spec did not ask for, that meets the blocker gate above with a demonstrated failure. An issue that is only `major` or `minor` — a coverage gap, a style nit, a pathological input — is reported with a `pass` verdict, and a review whose findings are all advisory passes with notes.");
|
|
52
|
+
lines.push("- `pass` only if you tried to break it and could not: every command listed under \"Run these commands\" exited 0, and no command you cite as evidence shows a failure this change caused.");
|
|
53
|
+
lines.push("- A probe of your own that could not run for a reason unrelated to the change (a missing tool, a wrong path, a sandbox limit) is not a failure of the change: name it in the summary with its exit code and leave the verdict alone.");
|
|
54
|
+
lines.push("- `inconclusive` only when a command listed under \"Run these commands\" itself could not run (missing dependencies, sandbox limits), or when there were none and you could not run the code at all. Say what you could not run.");
|
|
55
|
+
lines.push("- Severity: `blocker` = a defect that meets the gate above, citing the commandsRun entry that shows it; `major` = a correctness risk or a spec requirement without a demonstrated failure (static inspection, a missing test, a coverage gap); `minor` = advisory, including unsupported or pathological inputs.");
|
|
56
|
+
lines.push("- Every issue names a file and, when possible, the command or observation that demonstrates it in `evidence`. Do not report issues you did not confirm. In `evidence`, quote the command string exactly as it appears in commandsRun, or quote a line of its output verbatim (16 characters or more); a prose description of what a probe did cites nothing, and the adapter downgrades such a blocker to major.");
|
|
57
|
+
lines.push("- Do not pad. No issues and a `pass` verdict is a valid, good answer when the evidence supports it.");
|
|
58
|
+
lines.push("");
|
|
59
|
+
lines.push("Your final message must be only the JSON object described by the output schema: { verdict, summary, issues[] }.");
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ReviewResult, CommandRun, Sandbox, ReasoningEffort } from "./schema.js";
|
|
2
|
+
export interface ReviewOptions {
|
|
3
|
+
spec: string;
|
|
4
|
+
base: string;
|
|
5
|
+
/** Repository or worktree directory. Codex runs read-only inside it. */
|
|
6
|
+
dir: string;
|
|
7
|
+
head?: string;
|
|
8
|
+
model?: string;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
verifyCommands?: string[];
|
|
11
|
+
/** Executable name or path. Default "codex". Tests point this at a fake. */
|
|
12
|
+
codexBin?: string;
|
|
13
|
+
/** Keep the user's MCP servers from ~/.codex/config.toml. Default false: a reviewer needs none and they cost startup time. */
|
|
14
|
+
keepMcpServers?: boolean;
|
|
15
|
+
/** Extra `-c key=value` overrides passed through to codex. */
|
|
16
|
+
configOverrides?: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Codex sandbox. Default "workspace-write": test runners need scratch files (Vitest bundles its config next to it,
|
|
19
|
+
* which fails with EPERM under "read-only"). Codex keeps .git read-only in both modes, and the adapter compares
|
|
20
|
+
* `git status` before and after: any change to the working tree makes the result "inconclusive" with kind
|
|
21
|
+
* "worktree-modified". Pass "read-only" for projects whose checks never write.
|
|
22
|
+
*/
|
|
23
|
+
sandbox?: Sandbox;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to pass `-c sandbox_workspace_write.network_access=true`, which lets a verify command bind a
|
|
26
|
+
* localhost port under the workspace-write sandbox. An explicit true/false wins; undefined applies the
|
|
27
|
+
* default rule in {@link resolveNetworkAccess}. Codex ignores the override under "read-only".
|
|
28
|
+
*/
|
|
29
|
+
networkAccess?: boolean;
|
|
30
|
+
/** Reasoning effort override, passed as `-c model_reasoning_effort=<level>`. Default: whatever the model normally uses. */
|
|
31
|
+
effort?: ReasoningEffort;
|
|
32
|
+
}
|
|
33
|
+
export declare const DEFAULT_TIMEOUT_MS: number;
|
|
34
|
+
export declare const DEFAULT_SANDBOX: Sandbox;
|
|
35
|
+
/** Environment variable the adapter sets on every codex it spawns, so a check that suite spawns can detect nesting and skip. */
|
|
36
|
+
export declare const REVIEW_MARKER = "DOUG_CODEX_REVIEW";
|
|
37
|
+
/** The `-c` override that lets a verify command bind a localhost port under the workspace-write sandbox. */
|
|
38
|
+
export declare const NETWORK_ACCESS_OVERRIDE = "sandbox_workspace_write.network_access=true";
|
|
39
|
+
/**
|
|
40
|
+
* Whether codexArgs should pass NETWORK_ACCESS_OVERRIDE: false whenever the effective sandbox is not
|
|
41
|
+
* "workspace-write" (Codex ignores the override under read-only, so it is never passed); otherwise an
|
|
42
|
+
* explicit `networkAccess` boolean wins; otherwise on when at least one verify command is given.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveNetworkAccess(opts: {
|
|
45
|
+
sandbox?: Sandbox;
|
|
46
|
+
verifyCommands?: string[];
|
|
47
|
+
networkAccess?: boolean;
|
|
48
|
+
}): boolean;
|
|
49
|
+
/** The `-c` override for a reasoning-effort level, e.g. `model_reasoning_effort=low`. */
|
|
50
|
+
export declare function reasoningEffortOverride(level: ReasoningEffort): string;
|
|
51
|
+
/** The argv for `codex exec`, exposed so tests can assert the sandbox and schema are always present. */
|
|
52
|
+
export declare function codexArgs(opts: {
|
|
53
|
+
schemaFile: string;
|
|
54
|
+
lastMessageFile: string;
|
|
55
|
+
dir: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
keepMcpServers?: boolean;
|
|
58
|
+
configOverrides?: string[];
|
|
59
|
+
sandbox?: Sandbox;
|
|
60
|
+
networkAccess?: boolean;
|
|
61
|
+
effort?: ReasoningEffort;
|
|
62
|
+
}): string[];
|
|
63
|
+
export declare function runCodexReview(opts: ReviewOptions): Promise<ReviewResult>;
|
|
64
|
+
/** Convenience for callers that only want the CommandRun list from a raw event log. */
|
|
65
|
+
export declare function commandsFromEvents(jsonl: string): CommandRun[];
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Runs `codex exec` read-only against a worktree and turns its event stream into a ReviewResult.
|
|
2
|
+
// Every failure mode yields verdict "inconclusive" with a typed error; nothing here ever reports a pass
|
|
3
|
+
// it did not observe.
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join, resolve } from "node:path";
|
|
8
|
+
import { buildPrompt, MAX_EMBEDDED_DIFF_BYTES } from "./prompt.js";
|
|
9
|
+
import { parseEvents, extractJsonObject } from "./parse.js";
|
|
10
|
+
import { changeFacts, statusSnapshot, checkedOutMismatch } from "./git.js";
|
|
11
|
+
import { MODEL_REVIEW_SCHEMA, validateModelReview } from "./schema.js";
|
|
12
|
+
import { enforceBlockerEvidence } from "./contract.js";
|
|
13
|
+
export const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
14
|
+
export const DEFAULT_SANDBOX = "workspace-write";
|
|
15
|
+
/** Environment variable the adapter sets on every codex it spawns, so a check that suite spawns can detect nesting and skip. */
|
|
16
|
+
export const REVIEW_MARKER = "DOUG_CODEX_REVIEW";
|
|
17
|
+
/** The `-c` override that lets a verify command bind a localhost port under the workspace-write sandbox. */
|
|
18
|
+
export const NETWORK_ACCESS_OVERRIDE = "sandbox_workspace_write.network_access=true";
|
|
19
|
+
/**
|
|
20
|
+
* Whether codexArgs should pass NETWORK_ACCESS_OVERRIDE: false whenever the effective sandbox is not
|
|
21
|
+
* "workspace-write" (Codex ignores the override under read-only, so it is never passed); otherwise an
|
|
22
|
+
* explicit `networkAccess` boolean wins; otherwise on when at least one verify command is given.
|
|
23
|
+
*/
|
|
24
|
+
export function resolveNetworkAccess(opts) {
|
|
25
|
+
const sandbox = opts.sandbox || DEFAULT_SANDBOX;
|
|
26
|
+
if (sandbox !== "workspace-write")
|
|
27
|
+
return false;
|
|
28
|
+
if (typeof opts.networkAccess === "boolean")
|
|
29
|
+
return opts.networkAccess;
|
|
30
|
+
return (opts.verifyCommands || []).length > 0;
|
|
31
|
+
}
|
|
32
|
+
/** The `-c` override for a reasoning-effort level, e.g. `model_reasoning_effort=low`. */
|
|
33
|
+
export function reasoningEffortOverride(level) {
|
|
34
|
+
return `model_reasoning_effort=${level}`;
|
|
35
|
+
}
|
|
36
|
+
/** The argv for `codex exec`, exposed so tests can assert the sandbox and schema are always present. */
|
|
37
|
+
export function codexArgs(opts) {
|
|
38
|
+
const args = ["exec", "--json", "--ephemeral", "--sandbox", opts.sandbox || DEFAULT_SANDBOX, "--skip-git-repo-check", "--color", "never", "-C", opts.dir, "--output-schema", opts.schemaFile, "-o", opts.lastMessageFile];
|
|
39
|
+
if (opts.model)
|
|
40
|
+
args.push("-m", opts.model);
|
|
41
|
+
if (!opts.keepMcpServers)
|
|
42
|
+
args.push("-c", "mcp_servers={}");
|
|
43
|
+
// Pushed before the caller's configOverrides, so an explicit caller override for the same key comes later.
|
|
44
|
+
// That a later -c wins for a repeated key in codex is assumed, unverified against codex-cli 0.155.1.
|
|
45
|
+
if (opts.networkAccess)
|
|
46
|
+
args.push("-c", NETWORK_ACCESS_OVERRIDE);
|
|
47
|
+
if (opts.effort)
|
|
48
|
+
args.push("-c", reasoningEffortOverride(opts.effort));
|
|
49
|
+
for (const o of opts.configOverrides || [])
|
|
50
|
+
args.push("-c", o);
|
|
51
|
+
args.push("-"); // prompt on stdin: no argv length limit, no shell quoting
|
|
52
|
+
return args;
|
|
53
|
+
}
|
|
54
|
+
export async function runCodexReview(opts) {
|
|
55
|
+
const started = Date.now();
|
|
56
|
+
const dir = resolve(opts.dir);
|
|
57
|
+
const head = opts.head || "HEAD";
|
|
58
|
+
const inherited = !!process.env[REVIEW_MARKER];
|
|
59
|
+
const base = {
|
|
60
|
+
base: opts.base,
|
|
61
|
+
head,
|
|
62
|
+
dir,
|
|
63
|
+
reviewer: "codex",
|
|
64
|
+
model: opts.model || null,
|
|
65
|
+
sandbox: opts.sandbox || DEFAULT_SANDBOX,
|
|
66
|
+
marker: { name: REVIEW_MARKER, passed: false, inherited },
|
|
67
|
+
};
|
|
68
|
+
const fail = (error, extra = {}) => ({
|
|
69
|
+
...base,
|
|
70
|
+
verdict: "inconclusive",
|
|
71
|
+
summary: error.message,
|
|
72
|
+
issues: [],
|
|
73
|
+
commandsRun: [],
|
|
74
|
+
changedFiles: [],
|
|
75
|
+
error,
|
|
76
|
+
codexExitCode: null,
|
|
77
|
+
usage: null,
|
|
78
|
+
...extra,
|
|
79
|
+
durationMs: Date.now() - started,
|
|
80
|
+
});
|
|
81
|
+
let facts;
|
|
82
|
+
let before;
|
|
83
|
+
let mismatch;
|
|
84
|
+
try {
|
|
85
|
+
facts = changeFacts(dir, opts.base, head);
|
|
86
|
+
mismatch = checkedOutMismatch(dir, head);
|
|
87
|
+
before = statusSnapshot(dir);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
const msg = String(e && e.stderr ? e.stderr : e && e.message ? e.message : e).trim();
|
|
91
|
+
return fail({ kind: "git", message: `git diff ${opts.base}...${head} failed in ${dir}: ${msg}` });
|
|
92
|
+
}
|
|
93
|
+
if (mismatch)
|
|
94
|
+
return fail({ kind: "git", message: mismatch }, { changedFiles: facts.changedFiles });
|
|
95
|
+
const prompt = buildPrompt({
|
|
96
|
+
spec: opts.spec,
|
|
97
|
+
base: opts.base,
|
|
98
|
+
head,
|
|
99
|
+
changedFiles: facts.changedFiles,
|
|
100
|
+
diff: facts.diffBytes <= MAX_EMBEDDED_DIFF_BYTES ? facts.diff : null,
|
|
101
|
+
diffBytes: facts.diffBytes,
|
|
102
|
+
verifyCommands: opts.verifyCommands,
|
|
103
|
+
});
|
|
104
|
+
const tmp = mkdtempSync(join(tmpdir(), "codex-review-"));
|
|
105
|
+
const schemaFile = join(tmp, "schema.json");
|
|
106
|
+
const lastMessageFile = join(tmp, "last.txt");
|
|
107
|
+
writeFileSync(schemaFile, JSON.stringify(MODEL_REVIEW_SCHEMA));
|
|
108
|
+
const networkAccess = resolveNetworkAccess({ sandbox: opts.sandbox, verifyCommands: opts.verifyCommands, networkAccess: opts.networkAccess });
|
|
109
|
+
const args = codexArgs({ schemaFile, lastMessageFile, dir, model: opts.model, keepMcpServers: opts.keepMcpServers, configOverrides: opts.configOverrides, sandbox: opts.sandbox, networkAccess, effort: opts.effort });
|
|
110
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
111
|
+
const spawned = await spawnCollect(opts.codexBin || "codex", args, prompt, dir, timeoutMs);
|
|
112
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
113
|
+
const parsed = parseEvents(spawned.stdout);
|
|
114
|
+
const withEvidence = { commandsRun: parsed.commandsRun, changedFiles: facts.changedFiles, usage: parsed.usage, codexExitCode: spawned.exitCode, marker: { name: REVIEW_MARKER, passed: true, inherited } };
|
|
115
|
+
if (spawned.notFound)
|
|
116
|
+
return fail({ kind: "codex-not-found", message: `${opts.codexBin || "codex"} is not installed or not on PATH` }, { changedFiles: facts.changedFiles, marker: { name: REVIEW_MARKER, passed: true, inherited } });
|
|
117
|
+
// A reviewer that changed what it reviewed has no verdict worth reading, whatever it says.
|
|
118
|
+
let after = [];
|
|
119
|
+
try {
|
|
120
|
+
after = statusSnapshot(dir);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
after = before;
|
|
124
|
+
}
|
|
125
|
+
const changes = [...after.filter((l) => !before.includes(l)).map((l) => `+ ${l}`), ...before.filter((l) => !after.includes(l)).map((l) => `- ${l}`)];
|
|
126
|
+
if (changes.length) {
|
|
127
|
+
return fail({ kind: "worktree-modified", message: `the reviewer changed the working tree in ${dir}: ${changes.join("; ")}`, changes }, withEvidence);
|
|
128
|
+
}
|
|
129
|
+
if (spawned.timedOut)
|
|
130
|
+
return fail({ kind: "timeout", message: `codex exec exceeded ${timeoutMs} ms and was killed` }, withEvidence);
|
|
131
|
+
if (spawned.exitCode !== 0) {
|
|
132
|
+
// Codex reports API errors as JSONL `error` events on stdout; stderr is usually empty.
|
|
133
|
+
const detail = parsed.errors.length ? parsed.errors.join("\n") : spawned.stderr;
|
|
134
|
+
return fail({ kind: "codex-failed", message: `codex exec exited ${spawned.exitCode}${parsed.errors.length ? `: ${parsed.errors[0].slice(0, 300)}` : ""}`, stderrTail: detail.slice(-2000) }, withEvidence);
|
|
135
|
+
}
|
|
136
|
+
if (parsed.finalMessage === null)
|
|
137
|
+
return fail({ kind: "no-final-message", message: "codex produced no final agent message" }, withEvidence);
|
|
138
|
+
const obj = extractJsonObject(parsed.finalMessage);
|
|
139
|
+
if (obj === null)
|
|
140
|
+
return fail({ kind: "unparseable", message: "final message is not JSON", raw: parsed.finalMessage.slice(0, 4000) }, withEvidence);
|
|
141
|
+
const validated = validateModelReview(obj);
|
|
142
|
+
if ("error" in validated)
|
|
143
|
+
return fail({ kind: "unparseable", message: validated.error, raw: parsed.finalMessage.slice(0, 4000) }, withEvidence);
|
|
144
|
+
// R12 is enforced here, on the runtime's own commandsRun, never the model's claims: an evidence-free
|
|
145
|
+
// blocker is downgraded to major rather than trusted or silently dropped (docs/worker-contract.md rule 8).
|
|
146
|
+
return enforceBlockerEvidence({
|
|
147
|
+
...base,
|
|
148
|
+
...validated.review,
|
|
149
|
+
commandsRun: parsed.commandsRun,
|
|
150
|
+
changedFiles: facts.changedFiles,
|
|
151
|
+
error: null,
|
|
152
|
+
codexExitCode: spawned.exitCode,
|
|
153
|
+
usage: parsed.usage,
|
|
154
|
+
durationMs: Date.now() - started,
|
|
155
|
+
marker: { name: REVIEW_MARKER, passed: true, inherited },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function spawnCollect(bin, args, stdin, cwd, timeoutMs) {
|
|
159
|
+
return new Promise((res) => {
|
|
160
|
+
let stdout = "";
|
|
161
|
+
let stderr = "";
|
|
162
|
+
let notFound = false;
|
|
163
|
+
let timedOut = false;
|
|
164
|
+
let settled = false;
|
|
165
|
+
const child = spawn(bin, args, { cwd, stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NO_COLOR: "1", [REVIEW_MARKER]: "1" } });
|
|
166
|
+
const timer = setTimeout(() => {
|
|
167
|
+
timedOut = true;
|
|
168
|
+
child.kill("SIGKILL");
|
|
169
|
+
}, timeoutMs);
|
|
170
|
+
const finish = (exitCode) => {
|
|
171
|
+
if (settled)
|
|
172
|
+
return;
|
|
173
|
+
settled = true;
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
res({ stdout, stderr, exitCode, notFound, timedOut });
|
|
176
|
+
};
|
|
177
|
+
child.stdout.setEncoding("utf8").on("data", (d) => (stdout += d));
|
|
178
|
+
child.stderr.setEncoding("utf8").on("data", (d) => (stderr += d));
|
|
179
|
+
child.on("error", (err) => {
|
|
180
|
+
if (err.code === "ENOENT")
|
|
181
|
+
notFound = true;
|
|
182
|
+
else
|
|
183
|
+
stderr += String(err.message);
|
|
184
|
+
finish(null);
|
|
185
|
+
});
|
|
186
|
+
child.on("close", (code) => finish(code));
|
|
187
|
+
child.stdin.on("error", () => { });
|
|
188
|
+
child.stdin.end(stdin);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/** Convenience for callers that only want the CommandRun list from a raw event log. */
|
|
192
|
+
export function commandsFromEvents(jsonl) {
|
|
193
|
+
return parseEvents(jsonl).commandsRun;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|