@tokenfactory/acc-runner 0.29.4 → 0.33.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/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -2
- package/dist/config.js.map +1 -1
- package/dist/doctor.d.ts +19 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +169 -8
- package/dist/doctor.js.map +1 -1
- package/dist/engines/claude-code.d.ts +9 -0
- package/dist/engines/claude-code.d.ts.map +1 -0
- package/dist/engines/claude-code.js +95 -0
- package/dist/engines/claude-code.js.map +1 -0
- package/dist/engines/codex.d.ts +27 -0
- package/dist/engines/codex.d.ts.map +1 -0
- package/dist/engines/codex.js +178 -0
- package/dist/engines/codex.js.map +1 -0
- package/dist/engines/registry.d.ts +18 -0
- package/dist/engines/registry.d.ts.map +1 -0
- package/dist/engines/registry.js +26 -0
- package/dist/engines/registry.js.map +1 -0
- package/dist/engines/types.d.ts +73 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +2 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/failure-classifier.d.ts +32 -1
- package/dist/failure-classifier.d.ts.map +1 -1
- package/dist/failure-classifier.js +193 -1
- package/dist/failure-classifier.js.map +1 -1
- package/dist/git.d.ts +10 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +6 -0
- package/dist/git.js.map +1 -1
- package/dist/login.d.ts +17 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +22 -1
- package/dist/login.js.map +1 -1
- package/dist/pr-body.d.ts +53 -0
- package/dist/pr-body.d.ts.map +1 -0
- package/dist/pr-body.js +154 -0
- package/dist/pr-body.js.map +1 -0
- package/dist/prepr-gate.d.ts +132 -2
- package/dist/prepr-gate.d.ts.map +1 -1
- package/dist/prepr-gate.js +260 -9
- package/dist/prepr-gate.js.map +1 -1
- package/dist/provider-auth.d.ts +120 -0
- package/dist/provider-auth.d.ts.map +1 -0
- package/dist/provider-auth.js +184 -0
- package/dist/provider-auth.js.map +1 -0
- package/dist/review-diff-guard.d.ts +12 -0
- package/dist/review-diff-guard.d.ts.map +1 -0
- package/dist/review-diff-guard.js +52 -0
- package/dist/review-diff-guard.js.map +1 -0
- package/dist/review-worktree-cleanup.d.ts +73 -0
- package/dist/review-worktree-cleanup.d.ts.map +1 -0
- package/dist/review-worktree-cleanup.js +103 -0
- package/dist/review-worktree-cleanup.js.map +1 -0
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +72 -29
- package/dist/runtime/reviewer.js.map +1 -1
- package/dist/runtime/worktree.d.ts.map +1 -1
- package/dist/runtime/worktree.js +8 -3
- package/dist/runtime/worktree.js.map +1 -1
- package/dist/task-runner.d.ts +1 -8
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +65 -66
- package/dist/task-runner.js.map +1 -1
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +30 -3
- package/dist/watch.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2 (ENGINE-EXTRACT): the `claude-code` engine — the only entry in the Wave 1
|
|
3
|
+
* registry and the default. Its methods are lifted VERBATIM from the inline
|
|
4
|
+
* logic that used to live in task-runner.ts (`buildClaudeArgs`,
|
|
5
|
+
* `defaultSpawnClaude`, `defaultHealthProbe`) and reviewer.ts
|
|
6
|
+
* (`defaultSpawnClaude`), plus the shared `classifyClaudeExit` /
|
|
7
|
+
* `parseClaudeJson` calls. Output is byte-identical to pre-refactor — the
|
|
8
|
+
* golden tests in tests/engines-claude-code.test.ts lock that.
|
|
9
|
+
*/
|
|
10
|
+
import { execa } from "execa";
|
|
11
|
+
import { withAnthropicAuth } from "../provider-auth.js";
|
|
12
|
+
import { classifyClaudeExit } from "../failure-classifier.js";
|
|
13
|
+
import { parseClaudeJson } from "../cost-pricing.js";
|
|
14
|
+
const BINARY = "claude";
|
|
15
|
+
/**
|
|
16
|
+
* v0.6.0 REG-296: build the argv for `claude --print`. modelId is forwarded as
|
|
17
|
+
* `--model <id>` when non-empty (whitespace counts as empty); otherwise omitted
|
|
18
|
+
* so claude uses the operator's default model.
|
|
19
|
+
*/
|
|
20
|
+
export function buildClaudeArgs(modelId) {
|
|
21
|
+
const args = ["--print", "--dangerously-skip-permissions", "--output-format=json"];
|
|
22
|
+
const trimmed = (modelId ?? "").trim();
|
|
23
|
+
if (trimmed)
|
|
24
|
+
args.push("--model", trimmed);
|
|
25
|
+
return args;
|
|
26
|
+
}
|
|
27
|
+
/** withAnthropicAuth forwards ANTHROPIC_API_KEY (trimmed) when set — see src/provider-auth.ts. */
|
|
28
|
+
function claudeCodeEnv(base = process.env) {
|
|
29
|
+
return withAnthropicAuth(base);
|
|
30
|
+
}
|
|
31
|
+
function spawnClaudeCode(opts) {
|
|
32
|
+
// --dangerously-skip-permissions: bypass claude's per-edit approval prompts.
|
|
33
|
+
// The runner is non-interactive (no TTY for human approval) and the
|
|
34
|
+
// permission boundary is enforced one level up — see the "Runner sandbox"
|
|
35
|
+
// section in packages/acc-runner/README.md.
|
|
36
|
+
// --output-format=json: emit a structured object so we can parse the usage
|
|
37
|
+
// block for cost tracking. parseResult falls back to plain text when older
|
|
38
|
+
// claude versions emit markdown directly.
|
|
39
|
+
// --model: forwarded when the task pins a model (REG-296).
|
|
40
|
+
const { cwd, modelId, baseEnv } = opts;
|
|
41
|
+
return execa(BINARY, buildClaudeArgs(modelId), {
|
|
42
|
+
...(cwd !== undefined ? { cwd } : {}),
|
|
43
|
+
stdin: "pipe",
|
|
44
|
+
stdout: "pipe",
|
|
45
|
+
stderr: "pipe",
|
|
46
|
+
reject: false,
|
|
47
|
+
env: claudeCodeEnv(baseEnv),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* v0.63 T-63-1: default claude health probe. A silent instant-empty exit is
|
|
52
|
+
* ambiguous — it can be a broken host OR a momentarily-unavailable claude.
|
|
53
|
+
* `claude --version` is a cheap, deterministic answer to "is the binary/host
|
|
54
|
+
* actually broken?": if it returns 0 the host is fine (the exit was transient
|
|
55
|
+
* → claude_unavailable, retry), and any spawn failure / non-zero exit / timeout
|
|
56
|
+
* means the binary or its dynamic deps are genuinely broken (→ env_broken,
|
|
57
|
+
* quarantine). Short timeout so a probe never wedges the run.
|
|
58
|
+
*/
|
|
59
|
+
async function claudeCodeHealthProbe() {
|
|
60
|
+
try {
|
|
61
|
+
const res = await execa(BINARY, ["--version"], {
|
|
62
|
+
reject: false,
|
|
63
|
+
timeout: 5_000,
|
|
64
|
+
env: process.env,
|
|
65
|
+
});
|
|
66
|
+
if (res.exitCode === 0) {
|
|
67
|
+
return { ok: true, detail: (res.stdout || "").trim().slice(0, 200) || "claude --version ok" };
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
detail: `claude --version exited ${res.exitCode ?? "signal"}: ${(res.stderr || "").trim().slice(0, 200)}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return { ok: false, detail: `claude --version spawn failed: ${err.message}` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function classifyClaudeCodeFailure(exitCode, stderr, stdout, durationMs) {
|
|
79
|
+
return classifyClaudeExit(exitCode, stderr, stdout, durationMs);
|
|
80
|
+
}
|
|
81
|
+
function parseClaudeCodeResult(stdout) {
|
|
82
|
+
return parseClaudeJson(stdout);
|
|
83
|
+
}
|
|
84
|
+
export const claudeCodeEngine = {
|
|
85
|
+
id: "claude-code",
|
|
86
|
+
binary: BINARY,
|
|
87
|
+
authMode: "api-key-or-oauth",
|
|
88
|
+
buildArgs: buildClaudeArgs,
|
|
89
|
+
env: claudeCodeEnv,
|
|
90
|
+
spawn: spawnClaudeCode,
|
|
91
|
+
parseResult: parseClaudeCodeResult,
|
|
92
|
+
classifyFailure: classifyClaudeCodeFailure,
|
|
93
|
+
healthProbe: claudeCodeHealthProbe,
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/engines/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CodexAuthMode } from "../provider-auth.js";
|
|
2
|
+
import type { AgentEngine } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Feature flag: `engines.codex_enabled`. DEFAULT OFF. Truthy env forms only
|
|
5
|
+
* ("1"/"true"/"yes"/"on"). Kept as an env read (config.ts is out of this
|
|
6
|
+
* track's scope) so the engine stays fully self-contained and dead-until-wired.
|
|
7
|
+
*/
|
|
8
|
+
export declare function codexEngineEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Build the argv for a headless one-shot `codex exec`. `--json` emits the
|
|
11
|
+
* structured event stream parseResult reads; the approvals/sandbox bypass is
|
|
12
|
+
* the codex analog of claude's `--dangerously-skip-permissions` — the runner is
|
|
13
|
+
* non-interactive and enforces the permission boundary one level up (worktree
|
|
14
|
+
* isolation + prepr-gate scope + zero-diff guard), so codex must not block on
|
|
15
|
+
* its own approval prompts. `--model <id>` is forwarded only when non-blank.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildCodexArgs(modelId?: string | null): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the auth channel a codex spawn should START in from the environment:
|
|
20
|
+
* `ACC_CODEX_AUTH_PREFERENCE` (`subscription_first` default) + whether an
|
|
21
|
+
* OPENAI_API_KEY is present. The subscription→api-key SPILL on a cap-hit is
|
|
22
|
+
* driven by the (M5) task-runner, which re-invokes env()/spawn() with the mode
|
|
23
|
+
* pinned to "api-key" — so this only decides the first attempt.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveCodexSpawnMode(base?: NodeJS.ProcessEnv): CodexAuthMode;
|
|
26
|
+
export declare const codexEngine: AgentEngine;
|
|
27
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/engines/codex.ts"],"names":[],"mappings":"AAqBA,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AAIhF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAGhF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAKhE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,CAG1F;AAiHD,eAAO,MAAM,WAAW,EAAE,WAUzB,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4 (ENGINE-CODEX): the `codex` engine — the first non-Anthropic build
|
|
3
|
+
* engine, driving the OpenAI Codex CLI (`codex exec --json`). This is the
|
|
4
|
+
* HIGHEST-RISK track in the multi-model program: a mis-mapped OpenAI quota
|
|
5
|
+
* error would re-create the capacity pause-loop deadlock with a new provider
|
|
6
|
+
* (plan §0.1.5 / §3.3), so the failure classification (classifyCodexExit in
|
|
7
|
+
* failure-classifier.ts) is the P0 surface and is exhaustively fixture-tested.
|
|
8
|
+
*
|
|
9
|
+
* SHIPS FLAG-OFF. `codexEngineEnabled()` defaults false and the engine is NOT
|
|
10
|
+
* registered in engines/registry.ts, so the default fleet is byte-for-byte
|
|
11
|
+
* unaffected. Wiring the flag into the registry + the subscription→api-key
|
|
12
|
+
* spill orchestration is M5 (dispatch); this track only ships the engine, its
|
|
13
|
+
* classification, its auth resolution, and doctor validation.
|
|
14
|
+
*
|
|
15
|
+
* Flags/output are pinned to Codex CLI v0.44+; parseResult is deliberately
|
|
16
|
+
* TOLERANT (the v0.44 tolerant-parser lesson) so a point-release event-shape
|
|
17
|
+
* drift degrades to best-effort extraction rather than a hard failure.
|
|
18
|
+
*/
|
|
19
|
+
import { execa } from "execa";
|
|
20
|
+
import { classifyCodexExit } from "../failure-classifier.js";
|
|
21
|
+
import { openaiApiKey, resolveCodexAuthPreference, initialCodexAuthMode, withOpenAiAuth, } from "../provider-auth.js";
|
|
22
|
+
const BINARY = "codex";
|
|
23
|
+
/**
|
|
24
|
+
* Feature flag: `engines.codex_enabled`. DEFAULT OFF. Truthy env forms only
|
|
25
|
+
* ("1"/"true"/"yes"/"on"). Kept as an env read (config.ts is out of this
|
|
26
|
+
* track's scope) so the engine stays fully self-contained and dead-until-wired.
|
|
27
|
+
*/
|
|
28
|
+
export function codexEngineEnabled(env = process.env) {
|
|
29
|
+
const v = (env.ACC_ENGINE_CODEX_ENABLED ?? "").trim().toLowerCase();
|
|
30
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the argv for a headless one-shot `codex exec`. `--json` emits the
|
|
34
|
+
* structured event stream parseResult reads; the approvals/sandbox bypass is
|
|
35
|
+
* the codex analog of claude's `--dangerously-skip-permissions` — the runner is
|
|
36
|
+
* non-interactive and enforces the permission boundary one level up (worktree
|
|
37
|
+
* isolation + prepr-gate scope + zero-diff guard), so codex must not block on
|
|
38
|
+
* its own approval prompts. `--model <id>` is forwarded only when non-blank.
|
|
39
|
+
*/
|
|
40
|
+
export function buildCodexArgs(modelId) {
|
|
41
|
+
const args = ["exec", "--json", "--dangerously-bypass-approvals-and-sandbox"];
|
|
42
|
+
const trimmed = (modelId ?? "").trim();
|
|
43
|
+
if (trimmed)
|
|
44
|
+
args.push("--model", trimmed);
|
|
45
|
+
return args;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the auth channel a codex spawn should START in from the environment:
|
|
49
|
+
* `ACC_CODEX_AUTH_PREFERENCE` (`subscription_first` default) + whether an
|
|
50
|
+
* OPENAI_API_KEY is present. The subscription→api-key SPILL on a cap-hit is
|
|
51
|
+
* driven by the (M5) task-runner, which re-invokes env()/spawn() with the mode
|
|
52
|
+
* pinned to "api-key" — so this only decides the first attempt.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveCodexSpawnMode(base = process.env) {
|
|
55
|
+
const preference = resolveCodexAuthPreference(base.ACC_CODEX_AUTH_PREFERENCE);
|
|
56
|
+
return initialCodexAuthMode(preference, openaiApiKey(base).length > 0);
|
|
57
|
+
}
|
|
58
|
+
/** Augment the env for the resolved (first-attempt) auth channel. */
|
|
59
|
+
function codexEnv(base = process.env) {
|
|
60
|
+
return withOpenAiAuth(resolveCodexSpawnMode(base), base);
|
|
61
|
+
}
|
|
62
|
+
function spawnCodex(opts) {
|
|
63
|
+
const { cwd, modelId, baseEnv } = opts;
|
|
64
|
+
return execa(BINARY, buildCodexArgs(modelId), {
|
|
65
|
+
...(cwd !== undefined ? { cwd } : {}),
|
|
66
|
+
stdin: "pipe",
|
|
67
|
+
stdout: "pipe",
|
|
68
|
+
stderr: "pipe",
|
|
69
|
+
reject: false,
|
|
70
|
+
env: codexEnv(baseEnv),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function asRecord(v) {
|
|
74
|
+
return typeof v === "object" && v !== null ? v : null;
|
|
75
|
+
}
|
|
76
|
+
/** First non-empty string among `obj[key]` for the given candidate keys. */
|
|
77
|
+
function pickString(obj, keys) {
|
|
78
|
+
for (const k of keys) {
|
|
79
|
+
const v = obj[k];
|
|
80
|
+
if (typeof v === "string" && v.trim())
|
|
81
|
+
return v;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** Map a codex/OpenAI usage object onto the shared ClaudeUsage shape. */
|
|
86
|
+
function pickUsage(obj) {
|
|
87
|
+
const u = asRecord(obj.usage);
|
|
88
|
+
if (!u)
|
|
89
|
+
return null;
|
|
90
|
+
const inTok = u.input_tokens ?? u.prompt_tokens;
|
|
91
|
+
const outTok = u.output_tokens ?? u.completion_tokens;
|
|
92
|
+
const usage = {};
|
|
93
|
+
if (typeof inTok === "number")
|
|
94
|
+
usage.input_tokens = inTok;
|
|
95
|
+
if (typeof outTok === "number")
|
|
96
|
+
usage.output_tokens = outTok;
|
|
97
|
+
return usage.input_tokens !== undefined || usage.output_tokens !== undefined ? usage : null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Tolerant parse of `codex exec --json` output into the shared result envelope.
|
|
101
|
+
* Codex emits a JSONL event STREAM (unlike claude's single object), so we scan
|
|
102
|
+
* every parseable line and keep the LAST assistant-message text, model, and
|
|
103
|
+
* usage seen — the final turn's values. A single-object output (older/newer
|
|
104
|
+
* codex) is handled by the same scan. Returns null when nothing is parseable so
|
|
105
|
+
* the caller falls back to plain-text extraction, exactly like parseClaudeJson.
|
|
106
|
+
*/
|
|
107
|
+
function parseCodexResult(stdout) {
|
|
108
|
+
const trimmed = stdout.trim();
|
|
109
|
+
if (!trimmed)
|
|
110
|
+
return null;
|
|
111
|
+
const out = {};
|
|
112
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
113
|
+
const s = line.trim();
|
|
114
|
+
if (!s.startsWith("{"))
|
|
115
|
+
continue;
|
|
116
|
+
let parsed;
|
|
117
|
+
try {
|
|
118
|
+
parsed = JSON.parse(s);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const obj = asRecord(parsed);
|
|
124
|
+
if (!obj)
|
|
125
|
+
continue;
|
|
126
|
+
// The message text can live at the top level or nested under item/msg/response.
|
|
127
|
+
const nested = asRecord(obj.item) ?? asRecord(obj.msg) ?? asRecord(obj.response);
|
|
128
|
+
const text = pickString(obj, ["result", "text", "message", "content"]) ??
|
|
129
|
+
(nested ? pickString(nested, ["text", "message", "content", "output_text"]) : null);
|
|
130
|
+
if (text)
|
|
131
|
+
out.result = text;
|
|
132
|
+
const model = pickString(obj, ["model"]) ?? (nested ? pickString(nested, ["model"]) : null);
|
|
133
|
+
if (model)
|
|
134
|
+
out.model = model;
|
|
135
|
+
const usage = pickUsage(obj) ?? (nested ? pickUsage(nested) : null);
|
|
136
|
+
if (usage)
|
|
137
|
+
out.usage = usage;
|
|
138
|
+
}
|
|
139
|
+
return out.result !== undefined || out.model !== undefined || out.usage !== undefined
|
|
140
|
+
? out
|
|
141
|
+
: null;
|
|
142
|
+
}
|
|
143
|
+
function classifyCodexFailure(exitCode, stderr, stdout, durationMs) {
|
|
144
|
+
return classifyCodexExit(exitCode, stderr, stdout, durationMs);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Cheap health check: `codex --version`. Same contract as the claude probe —
|
|
148
|
+
* exit 0 means the binary/host is fine (a silent instant-empty exit was
|
|
149
|
+
* transient → engine_unavailable, retry), any spawn failure / non-zero exit /
|
|
150
|
+
* timeout means the binary or its deps are genuinely broken (→ env_broken).
|
|
151
|
+
*/
|
|
152
|
+
async function codexHealthProbe() {
|
|
153
|
+
try {
|
|
154
|
+
const res = await execa(BINARY, ["--version"], { reject: false, timeout: 5_000, env: process.env });
|
|
155
|
+
if (res.exitCode === 0) {
|
|
156
|
+
return { ok: true, detail: (res.stdout || "").trim().slice(0, 200) || "codex --version ok" };
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
detail: `codex --version exited ${res.exitCode ?? "signal"}: ${(res.stderr || "").trim().slice(0, 200)}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
return { ok: false, detail: `codex --version spawn failed: ${err.message}` };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export const codexEngine = {
|
|
168
|
+
id: "codex",
|
|
169
|
+
binary: BINARY,
|
|
170
|
+
authMode: "api-key-or-subscription",
|
|
171
|
+
buildArgs: buildCodexArgs,
|
|
172
|
+
env: codexEnv,
|
|
173
|
+
spawn: spawnCodex,
|
|
174
|
+
parseResult: parseCodexResult,
|
|
175
|
+
classifyFailure: classifyCodexFailure,
|
|
176
|
+
healthProbe: codexHealthProbe,
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/engines/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2 (ENGINE-EXTRACT): the engine registry keyed by id. Wave 1 registers a
|
|
3
|
+
* single engine — `claude-code` — which is also the default. `getEngine()`
|
|
4
|
+
* with no id (or a blank id) returns the default, so the existing call sites
|
|
5
|
+
* that never selected an engine keep their exact behavior.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentEngine } from "./types.js";
|
|
8
|
+
/** The default engine id used when a caller does not pin one. */
|
|
9
|
+
export declare const DEFAULT_ENGINE_ID: string;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve an engine by id. A null/blank id resolves to the default. Throws on
|
|
12
|
+
* an unknown id — no Wave 1 call site passes one, so this is a guard for
|
|
13
|
+
* future multi-engine wiring, never a live path.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEngine(id?: string | null): AgentEngine;
|
|
16
|
+
export declare function hasEngine(id: string): boolean;
|
|
17
|
+
export declare function engineIds(): string[];
|
|
18
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/engines/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO9C,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAOzD;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAEpC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { claudeCodeEngine } from "./claude-code.js";
|
|
2
|
+
const ENGINES = {
|
|
3
|
+
[claudeCodeEngine.id]: claudeCodeEngine,
|
|
4
|
+
};
|
|
5
|
+
/** The default engine id used when a caller does not pin one. */
|
|
6
|
+
export const DEFAULT_ENGINE_ID = claudeCodeEngine.id;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve an engine by id. A null/blank id resolves to the default. Throws on
|
|
9
|
+
* an unknown id — no Wave 1 call site passes one, so this is a guard for
|
|
10
|
+
* future multi-engine wiring, never a live path.
|
|
11
|
+
*/
|
|
12
|
+
export function getEngine(id) {
|
|
13
|
+
const key = (id ?? "").trim() || DEFAULT_ENGINE_ID;
|
|
14
|
+
const engine = ENGINES[key];
|
|
15
|
+
if (!engine) {
|
|
16
|
+
throw new Error(`unknown agent engine: ${key} (known: ${Object.keys(ENGINES).join(", ")})`);
|
|
17
|
+
}
|
|
18
|
+
return engine;
|
|
19
|
+
}
|
|
20
|
+
export function hasEngine(id) {
|
|
21
|
+
return id in ENGINES;
|
|
22
|
+
}
|
|
23
|
+
export function engineIds() {
|
|
24
|
+
return Object.keys(ENGINES);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/engines/registry.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,OAAO,GAAgC;IAC3C,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,gBAAgB;CACxC,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EAAkB;IAC1C,MAAM,GAAG,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,iBAAiB,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,YAAY,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,IAAI,OAAO,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2 (ENGINE-EXTRACT): the `AgentEngine` interface abstracts the coding /
|
|
3
|
+
* review subprocess a runner drives — how to build its argv, augment its
|
|
4
|
+
* environment, spawn it, parse its structured result envelope, classify a
|
|
5
|
+
* failure into the shared class taxonomy, and probe whether the binary/host
|
|
6
|
+
* is healthy.
|
|
7
|
+
*
|
|
8
|
+
* Wave 1 ships exactly one implementation, `claude-code`, whose behavior is
|
|
9
|
+
* byte-identical to the pre-refactor inline logic that used to live in
|
|
10
|
+
* task-runner.ts and reviewer.ts. The interface + registry exist so a later
|
|
11
|
+
* wave can register additional engines without touching the drive loops.
|
|
12
|
+
*
|
|
13
|
+
* This is a PURE refactor: no DB value, telemetry value, or runner-protocol
|
|
14
|
+
* message shape changes. `failure-classifier.ts` remains the single shared
|
|
15
|
+
* class-mapper every engine's `classifyFailure` calls into.
|
|
16
|
+
*/
|
|
17
|
+
import type { ExecaChildProcess } from "execa";
|
|
18
|
+
import type { ClassifiedExit, ClaudeExitClass } from "../failure-classifier.js";
|
|
19
|
+
import type { ClaudeJsonOutput } from "../cost-pricing.js";
|
|
20
|
+
/**
|
|
21
|
+
* How an engine authenticates. Documentation + future gating.
|
|
22
|
+
* - claude-code: "api-key-or-oauth" (ANTHROPIC_API_KEY else interactive OAuth).
|
|
23
|
+
* - codex (M4): "api-key-or-subscription" — OPENAI_API_KEY (api-key channel)
|
|
24
|
+
* OR a stored ChatGPT login (subscription channel); the
|
|
25
|
+
* `subscription_first` preference tries subscription, then
|
|
26
|
+
* spills to api-key on a cap-hit (see src/provider-auth.ts).
|
|
27
|
+
*/
|
|
28
|
+
export type EngineAuthMode = "api-key-or-oauth" | "api-key-or-subscription" | "api-key" | "oauth" | "subscription" | "none";
|
|
29
|
+
/**
|
|
30
|
+
* The engine-agnostic failure taxonomy is `ClaudeExitClass` verbatim so the
|
|
31
|
+
* DB/telemetry values stay frozen. The claude-code class `claude_unavailable`
|
|
32
|
+
* is the engine's spelling of the generic "engine momentarily unavailable"
|
|
33
|
+
* class (aliased below); the emitted string value never changes.
|
|
34
|
+
*/
|
|
35
|
+
export type EngineFailureClass = ClaudeExitClass;
|
|
36
|
+
/** The low-level parse of the engine's structured stdout envelope (null = not the JSON form). */
|
|
37
|
+
export type EngineResult = ClaudeJsonOutput | null;
|
|
38
|
+
export interface EngineSpawnOptions {
|
|
39
|
+
/** Working directory for the child (task-runner sets the worktree; reviewer omits it → cwd unchanged). */
|
|
40
|
+
cwd?: string;
|
|
41
|
+
/** Model to pin via the engine's model flag; omitted/blank uses the operator default. */
|
|
42
|
+
modelId?: string | null;
|
|
43
|
+
/** Base environment to augment via env(); defaults to process.env. */
|
|
44
|
+
baseEnv?: NodeJS.ProcessEnv;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentEngine {
|
|
47
|
+
/** Registry key + DB/telemetry id. */
|
|
48
|
+
readonly id: string;
|
|
49
|
+
/** Executable name resolved on PATH. */
|
|
50
|
+
readonly binary: string;
|
|
51
|
+
/** How the engine authenticates (documentation + future gating). */
|
|
52
|
+
readonly authMode: EngineAuthMode;
|
|
53
|
+
/** Build the CLI argv for a one-shot `--print` run (model pinned when non-blank). */
|
|
54
|
+
buildArgs(modelId?: string | null): string[];
|
|
55
|
+
/** Augment a base environment (default process.env) with the engine's auth channel. */
|
|
56
|
+
env(base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
57
|
+
/** Spawn the engine as a piped child (stdin/stdout/stderr = pipe, reject = false). */
|
|
58
|
+
spawn(opts: EngineSpawnOptions): ExecaChildProcess;
|
|
59
|
+
/** Parse the structured result envelope from stdout (null when not the JSON form). */
|
|
60
|
+
parseResult(stdout: string): EngineResult;
|
|
61
|
+
/**
|
|
62
|
+
* Classify a non-zero (or 429-on-zero) exit into the shared class taxonomy.
|
|
63
|
+
* Delegates to failure-classifier — the one shared class-mapper all engines
|
|
64
|
+
* call into — so the DB class values stay frozen and backward-compatible.
|
|
65
|
+
*/
|
|
66
|
+
classifyFailure(exitCode: number | null, stderr: string, stdout: string, durationMs?: number): ClassifiedExit;
|
|
67
|
+
/** Cheap health check: is the binary/host actually working? */
|
|
68
|
+
healthProbe(): Promise<{
|
|
69
|
+
ok: boolean;
|
|
70
|
+
detail: string;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engines/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,yBAAyB,GACzB,SAAS,GACT,OAAO,GACP,cAAc,GACd,MAAM,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAEjD,iGAAiG;AACjG,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG,IAAI,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,0GAA0G;IAC1G,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAElC,qFAAqF;IACrF,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;IAE7C,uFAAuF;IACvF,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAEjD,sFAAsF;IACtF,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAAC;IAEnD,sFAAsF;IACtF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC;IAE1C;;;;OAIG;IACH,eAAe,CACb,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CAAC;IAElB,+DAA+D;IAC/D,WAAW,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engines/types.ts"],"names":[],"mappings":""}
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* instead (capacity until a reset time, claude_unavailable with an
|
|
43
43
|
* exponential backoff).
|
|
44
44
|
*/
|
|
45
|
-
export type ClaudeExitClass = "capacity_exhausted" | "claude_unavailable" | "usage_limit" | "auth_expired" | "env_broken" | "task_error";
|
|
45
|
+
export type ClaudeExitClass = "capacity_exhausted" | "subscription_exhausted" | "claude_unavailable" | "usage_limit" | "auth_expired" | "env_broken" | "task_error";
|
|
46
46
|
export interface ClassifiedExit {
|
|
47
47
|
exitCode: number | null;
|
|
48
48
|
class: ClaudeExitClass;
|
|
@@ -75,6 +75,21 @@ export interface ClassifiedExit {
|
|
|
75
75
|
* classifier stays a pure (strings) → class function for unit tests.
|
|
76
76
|
*/
|
|
77
77
|
export declare function parseApiErrorStatus(stdout: string): number | null;
|
|
78
|
+
/**
|
|
79
|
+
* v0.58 (T-5058, FH-1a): the capacity-exhaustion predicate, factored out of
|
|
80
|
+
* `classifyClaudeExit` so callers can detect a spent capacity window WITHOUT a
|
|
81
|
+
* non-zero exit code to key on. Claude's `--output-format=json` envelope can
|
|
82
|
+
* report `{ is_error: true, api_error_status: 429 }` while the CLI still exits
|
|
83
|
+
* 0 (the exit code and the JSON error are set independently, and the code is
|
|
84
|
+
* version-dependent). The exit-code path in the task-runner only classifies on
|
|
85
|
+
* a non-zero exit, so a 0-exit-with-429 would otherwise be mistaken for a
|
|
86
|
+
* successful build — pushing a partial worktree and opening a PR that burns the
|
|
87
|
+
* scarce reviewer capacity this class exists to protect. The authoritative
|
|
88
|
+
* signal is the JSON `api_error_status` 429; CAPACITY_PATTERNS is the
|
|
89
|
+
* plain-text fallback. Kept disjoint from USAGE_LIMIT_PATTERNS (hard billing
|
|
90
|
+
* walls that stay quarantine-worthy), exactly as `classifyClaudeExit` is.
|
|
91
|
+
*/
|
|
92
|
+
export declare function isCapacityExhausted(stdout: string, stderr?: string): boolean;
|
|
78
93
|
/**
|
|
79
94
|
* v0.56 (T-56-1): parse the reset time Claude states when it pauses a
|
|
80
95
|
* session ("...resets 3:20pm (Asia/Calcutta)") into an absolute epoch
|
|
@@ -109,4 +124,20 @@ export declare function classifyClaudeExit(exitCode: number | null, stderr: stri
|
|
|
109
124
|
* since the collision is transient and the task re-provisions cleanly on retry.
|
|
110
125
|
*/
|
|
111
126
|
export declare function isGitProvisionContention(message: string): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* M4: parse a retry-after hint from a codex rate-limit message ("Please try
|
|
129
|
+
* again in 20s" / "retry-after: 1500ms"). Returns the delay in MILLISECONDS,
|
|
130
|
+
* or null when no hint is present — the caller then applies a fixed default
|
|
131
|
+
* pause (pause-till-reset semantics). Bare numbers are read as seconds, matching
|
|
132
|
+
* OpenAI's Retry-After header contract.
|
|
133
|
+
*/
|
|
134
|
+
export declare function extractCodexRetryAfterMs(text: string): number | null;
|
|
135
|
+
/**
|
|
136
|
+
* M4 (ENGINE-CODEX): classify a codex CLI exit into the shared taxonomy. Same
|
|
137
|
+
* signature contract as `classifyClaudeExit` (the AgentEngine.classifyFailure
|
|
138
|
+
* shape) so the drive loop is engine-agnostic. See the block comment above for
|
|
139
|
+
* the ordering rationale — the billing-before-rate-limit order is the guard
|
|
140
|
+
* against the pause-loop deadlock.
|
|
141
|
+
*/
|
|
142
|
+
export declare function classifyCodexExit(exitCode: number | null, stderr: string, stdout: string, durationMs?: number): ClassifiedExit;
|
|
112
143
|
//# sourceMappingURL=failure-classifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;
|
|
1
|
+
{"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AA2BH,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,wBAAwB,GACxB,oBAAoB,GACpB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA0ED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,OAAO,CAGxE;AA2DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,IAAI,CAiCtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CAqFhB;AAwBD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGjE;AAwFD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUpE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CA2EhB"}
|