auto-mode 1.0.1 → 2.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/dist/cli.js +150 -12
- package/dist/index.d.ts +20 -1
- package/dist/index.js +1 -1
- package/dist/parse-action-request-DtReuNOc.js +5160 -0
- package/dist/{parse-action-request-BfkmQxEC.js → schemas-Dc2424NY.js} +83 -5238
- package/dist/start-judge-server-Drj5c8js.js +169 -0
- package/dist/start-judge-session-9R_bGhxY.js +37956 -0
- package/dist/start-judge-session-Ws6eqDt4.js +2 -0
- package/package.json +2 -1
- package/policy/containment.md +3 -3
- package/policy/judge.md +5 -12
- package/policy/classifier.md +0 -282
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as resolveXDGDir,
|
|
3
|
-
import {
|
|
2
|
+
import { C as resolveXDGDir, S as CLI_LIMITS, _ as findGitEntry, a as EMPTY_SESSION_SCOPE, b as readHostEnvironment, c as loadPolicy, d as writeActionDiagnostic, f as toRepositorySlug, g as findCheckout, h as isUnresolvedWord, i as loadSessionScope, l as resolveShippedPolicyDir, m as resolveShellPath, n as classifyAction, o as splitGitCommand, p as collectShellCommands, r as resolveSessionScopePath, s as M, t as parseActionRequest, u as loadCheckoutFacts, v as DEFAULT_SCOPE_SOURCES, w as updateJSONFile, x as runCapped, y as loadConfig } from "./parse-action-request-DtReuNOc.js";
|
|
3
|
+
import { a as literal, d as string, i as discriminatedUnion, r as boolean, s as number, u as strictObject } from "./schemas-Dc2424NY.js";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
5
|
import { chmod, mkdir, open, readFile, realpath, stat } from "node:fs/promises";
|
|
5
6
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
6
|
-
import {
|
|
7
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { constants, existsSync, statSync } from "node:fs";
|
|
7
10
|
import { text } from "node:stream/consumers";
|
|
8
11
|
import { parseArgs } from "node:util";
|
|
12
|
+
import { connect } from "node:net";
|
|
9
13
|
//#region src/capture/resolve-capture-dir.ts
|
|
10
14
|
function resolveCaptureDir(capture, host) {
|
|
11
15
|
return capture.dir ?? join(resolveXDGDir(host, "state"), "captures");
|
|
@@ -73,7 +77,6 @@ async function writeOwnerFile(path, text, flag) {
|
|
|
73
77
|
//#endregion
|
|
74
78
|
//#region src/config/build-jev-only-config.ts
|
|
75
79
|
function buildJevOnlyConfig(config) {
|
|
76
|
-
if (config.provider.protocol !== "system-one") return null;
|
|
77
80
|
const judge = config.judge === void 0 || config.judge === null ? config.judge : {
|
|
78
81
|
...config.judge,
|
|
79
82
|
timeoutMs: Math.min(config.judge.timeoutMs, CLI_LIMITS.judgeMs)
|
|
@@ -95,6 +98,117 @@ function resolveShippedModDir(startDir = import.meta.dirname) {
|
|
|
95
98
|
return modDir;
|
|
96
99
|
}
|
|
97
100
|
//#endregion
|
|
101
|
+
//#region src/process/start-detached-process.ts
|
|
102
|
+
function startDetachedProcess(file, args, env) {
|
|
103
|
+
const child = spawn(file, args, {
|
|
104
|
+
detached: true,
|
|
105
|
+
stdio: "ignore",
|
|
106
|
+
env
|
|
107
|
+
});
|
|
108
|
+
child.on("error", () => {});
|
|
109
|
+
child.unref();
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/judge/judge-server-response-schema.ts
|
|
113
|
+
const judgeServerResponseSchema = discriminatedUnion("kind", [strictObject({
|
|
114
|
+
kind: literal("reply"),
|
|
115
|
+
text: string(),
|
|
116
|
+
model: string(),
|
|
117
|
+
safetyStops: number().nullable(),
|
|
118
|
+
fallback: boolean(),
|
|
119
|
+
warm: boolean().nullable()
|
|
120
|
+
}), strictObject({
|
|
121
|
+
kind: literal("failure"),
|
|
122
|
+
aborted: boolean(),
|
|
123
|
+
message: string()
|
|
124
|
+
})]);
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/judge/make-judge-server-session.ts
|
|
127
|
+
function makeJudgeServerSession(target) {
|
|
128
|
+
return {
|
|
129
|
+
review: async (model, request, signal) => {
|
|
130
|
+
const answer = await sendLine(await tryConnectOrStartServer(target, signal), `${JSON.stringify({
|
|
131
|
+
model,
|
|
132
|
+
timeoutMs: target.timeoutMs,
|
|
133
|
+
...request
|
|
134
|
+
})}\n`, signal);
|
|
135
|
+
const response = judgeServerResponseSchema.parse(JSON.parse(answer));
|
|
136
|
+
if (response.kind === "failure") throw response.aborted ? new DOMException(response.message, "AbortError") : new Error(response.message);
|
|
137
|
+
const { kind: _kind, ...reply } = response;
|
|
138
|
+
return reply;
|
|
139
|
+
},
|
|
140
|
+
stop: () => {}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const START_WAIT_MS = 1e4;
|
|
144
|
+
const START_POLL_MS = 25;
|
|
145
|
+
async function tryConnectOrStartServer(target, signal) {
|
|
146
|
+
const existing = await tryConnect(target.socketPath);
|
|
147
|
+
if (existing !== null) return existing;
|
|
148
|
+
const [file, ...args] = target.command;
|
|
149
|
+
startDetachedProcess(file, [
|
|
150
|
+
...args,
|
|
151
|
+
"judge-server",
|
|
152
|
+
target.socketPath
|
|
153
|
+
], target.env);
|
|
154
|
+
const deadline = Date.now() + START_WAIT_MS;
|
|
155
|
+
while (Date.now() < deadline) {
|
|
156
|
+
await setTimeout(START_POLL_MS, void 0, { signal });
|
|
157
|
+
const socket = await tryConnect(target.socketPath);
|
|
158
|
+
if (socket !== null) return socket;
|
|
159
|
+
}
|
|
160
|
+
throw new Error("the judge server did not start");
|
|
161
|
+
}
|
|
162
|
+
function tryConnect(socketPath) {
|
|
163
|
+
return new Promise((resolve) => {
|
|
164
|
+
const socket = connect(socketPath);
|
|
165
|
+
socket.once("connect", () => {
|
|
166
|
+
socket.off("error", onError);
|
|
167
|
+
resolve(socket);
|
|
168
|
+
});
|
|
169
|
+
const onError = () => {
|
|
170
|
+
resolve(null);
|
|
171
|
+
};
|
|
172
|
+
socket.once("error", onError);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function sendLine(socket, line, signal) {
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
const chunks = [];
|
|
178
|
+
const onAbort = () => {
|
|
179
|
+
socket.destroy();
|
|
180
|
+
reject(new DOMException("Judge review aborted", "AbortError"));
|
|
181
|
+
};
|
|
182
|
+
if (signal.aborted) {
|
|
183
|
+
onAbort();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
187
|
+
socket.on("data", (chunk) => {
|
|
188
|
+
chunks.push(chunk);
|
|
189
|
+
});
|
|
190
|
+
socket.once("error", (error) => {
|
|
191
|
+
signal.removeEventListener("abort", onAbort);
|
|
192
|
+
reject(error);
|
|
193
|
+
});
|
|
194
|
+
socket.once("end", () => {
|
|
195
|
+
signal.removeEventListener("abort", onAbort);
|
|
196
|
+
socket.destroy();
|
|
197
|
+
resolve(Buffer.concat(chunks).toString("utf8").trim());
|
|
198
|
+
});
|
|
199
|
+
socket.write(line);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/judge/resolve-judge-socket-path.ts
|
|
204
|
+
function resolveJudgeSocketPath(host, sessionID, entry) {
|
|
205
|
+
const runtime = host.env["XDG_RUNTIME_DIR"];
|
|
206
|
+
const dir = runtime === void 0 || runtime === "" ? join(resolveXDGDir(host, "state"), "judge") : join(runtime, "auto-mode");
|
|
207
|
+
const build = statSync(entry, { throwIfNoEntry: false });
|
|
208
|
+
const name = createHash("sha256").update(`${sessionID}\0${entry}\0${String(build?.mtimeMs ?? 0)}`).digest("hex").slice(0, 24);
|
|
209
|
+
return join(dir, `judge-${name}.sock`);
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
98
212
|
//#region src/scope/read-pull-request.ts
|
|
99
213
|
const LOOKUP_TIMEOUT_MS = 5e3;
|
|
100
214
|
const MAX_LOOKUP_OUTPUT_BYTES = 65536;
|
|
@@ -419,6 +533,7 @@ Usage:
|
|
|
419
533
|
auto-mode print-prompt Print the system prompt the classifier receives
|
|
420
534
|
auto-mode record Read a finished Bash call on stdin, add what it created to the session's scope
|
|
421
535
|
auto-mode mod-path Print the directory of the Claude Code mod this package ships
|
|
536
|
+
auto-mode judge-server <socket> Serve one agent session's judge reviews on a unix socket
|
|
422
537
|
|
|
423
538
|
Options:
|
|
424
539
|
--classifier <path> Use this framework file instead of the shipped one
|
|
@@ -449,14 +564,14 @@ async function runCLI(argv, io) {
|
|
|
449
564
|
if (command === "print-prompt") {
|
|
450
565
|
const config = await loadConfig(void 0, io.host);
|
|
451
566
|
printWarnings(io.stderr, config.warnings);
|
|
452
|
-
const framework = config.provider.protocol === "system-one" ? "decision.md" : "classifier.md";
|
|
453
567
|
const prompt = await loadPolicy({
|
|
454
568
|
classifierPath: args.values.classifier ?? config.classifierPath,
|
|
455
569
|
rulesPath: args.values.rules ?? config.rulesPath
|
|
456
|
-
},
|
|
570
|
+
}, "decision.md");
|
|
457
571
|
io.stdout.write(`${prompt}\n`);
|
|
458
572
|
return 0;
|
|
459
573
|
}
|
|
574
|
+
if (command === "judge-server") return runJudgeServer(io, args.positionals[1]);
|
|
460
575
|
if (command === "mod-path") try {
|
|
461
576
|
io.stdout.write(`${resolveShippedModDir()}\n`);
|
|
462
577
|
return 0;
|
|
@@ -467,6 +582,31 @@ async function runCLI(argv, io) {
|
|
|
467
582
|
io.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
|
|
468
583
|
return 2;
|
|
469
584
|
}
|
|
585
|
+
function buildJudgeServerSession(io, config, sessionID) {
|
|
586
|
+
const judge = config.judge ?? null;
|
|
587
|
+
if (io.judgeServerCommand === void 0 || judge?.protocol !== "claude-code") return;
|
|
588
|
+
const command = io.judgeServerCommand;
|
|
589
|
+
const entry = command[1] ?? command[0];
|
|
590
|
+
return makeJudgeServerSession({
|
|
591
|
+
socketPath: resolveJudgeSocketPath(io.host, sessionID, entry),
|
|
592
|
+
command: io.judgeServerCommand,
|
|
593
|
+
env: io.host.env,
|
|
594
|
+
timeoutMs: judge.timeoutMs
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
async function runJudgeServer(io, socketPath) {
|
|
598
|
+
if (socketPath === void 0) {
|
|
599
|
+
io.stderr.write(`auto-mode: judge-server needs a socket path\n\n${USAGE}`);
|
|
600
|
+
return 2;
|
|
601
|
+
}
|
|
602
|
+
await (await (await import("./start-judge-server-Drj5c8js.js")).startJudgeServer({
|
|
603
|
+
socketPath,
|
|
604
|
+
env: io.host.env,
|
|
605
|
+
idleMs: JUDGE_IDLE_MS
|
|
606
|
+
}))?.closed;
|
|
607
|
+
return 0;
|
|
608
|
+
}
|
|
609
|
+
const JUDGE_IDLE_MS = 9e5;
|
|
470
610
|
function tryParseCLIArgs(argv) {
|
|
471
611
|
try {
|
|
472
612
|
return parseArgs({
|
|
@@ -509,10 +649,6 @@ async function run(io, explain, localOnly, jevOnly, deadline) {
|
|
|
509
649
|
}
|
|
510
650
|
printWarnings(io.stderr, loaded.warnings);
|
|
511
651
|
const config = jevOnly ? buildJevOnlyConfig(loaded) : loaded;
|
|
512
|
-
if (config === null) {
|
|
513
|
-
await writeActionFailure(request, host, io.stderr);
|
|
514
|
-
return printNote(io.stderr, true, "Jev-only evaluation requires system-one; no verdict");
|
|
515
|
-
}
|
|
516
652
|
const deadlineAt = deadline === void 0 ? void 0 : Number(deadline);
|
|
517
653
|
if (deadlineAt !== void 0 && (!jevOnly || !Number.isSafeInteger(deadlineAt) || deadlineAt <= 0)) {
|
|
518
654
|
await writeActionFailure(request, host, io.stderr);
|
|
@@ -530,7 +666,8 @@ async function run(io, explain, localOnly, jevOnly, deadline) {
|
|
|
530
666
|
host,
|
|
531
667
|
stderr: io.stderr,
|
|
532
668
|
budget: true,
|
|
533
|
-
actionLog: true
|
|
669
|
+
actionLog: true,
|
|
670
|
+
judgeSession: buildJudgeServerSession(io, config, request.sessionID)
|
|
534
671
|
});
|
|
535
672
|
if (outcome.verdict !== null) printVerdict(io.stdout, outcome.verdict);
|
|
536
673
|
if (config.capture?.enabled === true) {
|
|
@@ -594,7 +731,8 @@ process.exitCode = await runCLI(process.argv.slice(2), {
|
|
|
594
731
|
process.off("SIGTERM", onStop);
|
|
595
732
|
process.off("SIGINT", onStop);
|
|
596
733
|
};
|
|
597
|
-
}
|
|
734
|
+
},
|
|
735
|
+
judgeServerCommand: [process.execPath, import.meta.filename]
|
|
598
736
|
});
|
|
599
737
|
//#endregion
|
|
600
738
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,27 @@ interface DenialBudget {
|
|
|
4
4
|
readonly perSession: number;
|
|
5
5
|
}
|
|
6
6
|
//#endregion
|
|
7
|
+
//#region src/messages/anthropic-client.d.ts
|
|
8
|
+
interface ModelRequest {
|
|
9
|
+
readonly system: string;
|
|
10
|
+
readonly user: string;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
7
13
|
//#region src/judge/types.d.ts
|
|
8
14
|
type OverturnBasis = 'consent' | 'misread';
|
|
9
15
|
type JudgeOverturns = 'consent' | 'any';
|
|
10
16
|
type JudgeStatus = 'confirmed' | 'overturned' | 'held' | 'failed';
|
|
17
|
+
interface JudgeMessageReply {
|
|
18
|
+
readonly text: string;
|
|
19
|
+
readonly model: string;
|
|
20
|
+
readonly safetyStops: number | null;
|
|
21
|
+
readonly fallback: boolean;
|
|
22
|
+
readonly warm: boolean | null;
|
|
23
|
+
}
|
|
24
|
+
interface JudgeSession {
|
|
25
|
+
readonly review: (model: string, request: Readonly<ModelRequest>, signal: Readonly<AbortSignal>) => Promise<JudgeMessageReply>;
|
|
26
|
+
readonly stop: () => void;
|
|
27
|
+
}
|
|
11
28
|
type JudgeFailure = 'credential' | 'policy' | 'request' | 'timeout' | 'unreadable';
|
|
12
29
|
interface JudgeDiagnostics {
|
|
13
30
|
readonly status: JudgeStatus;
|
|
@@ -16,13 +33,14 @@ interface JudgeDiagnostics {
|
|
|
16
33
|
readonly model: string;
|
|
17
34
|
readonly answeringModel: string | null;
|
|
18
35
|
readonly safetyStops: number | null;
|
|
36
|
+
readonly warm: boolean | null;
|
|
19
37
|
readonly rule: string;
|
|
20
38
|
readonly tier: 'hard' | 'soft';
|
|
21
39
|
readonly elapsedMs: number;
|
|
22
40
|
}
|
|
23
41
|
//#endregion
|
|
24
42
|
//#region src/types.d.ts
|
|
25
|
-
type DecidingStage = 'local' | 'containment' | 'bypass' | 'jev' | 'judge' | '
|
|
43
|
+
type DecidingStage = 'local' | 'containment' | 'bypass' | 'jev' | 'judge' | 'retry' | 'budget';
|
|
26
44
|
interface DecisionRule {
|
|
27
45
|
readonly name: string;
|
|
28
46
|
readonly tier: 'hard' | 'soft';
|
|
@@ -216,6 +234,7 @@ interface ClassifyOptions extends EvaluationOptions {
|
|
|
216
234
|
readonly actionLog?: boolean;
|
|
217
235
|
readonly stderr?: Readonly<OutputStream> | undefined;
|
|
218
236
|
readonly findCheckout?: CheckoutFinder | undefined;
|
|
237
|
+
readonly judgeSession?: JudgeSession | undefined;
|
|
219
238
|
}
|
|
220
239
|
export declare function classifyAction(request: ActionRequest, config: Config, options?: ClassifyOptions): Promise<ActionOutcome>;
|
|
221
240
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as classifyAction, t as parseActionRequest, y as loadConfig } from "./parse-action-request-
|
|
1
|
+
import { n as classifyAction, t as parseActionRequest, y as loadConfig } from "./parse-action-request-DtReuNOc.js";
|
|
2
2
|
export { classifyAction, loadConfig, parseActionRequest };
|