auto-mode 0.15.0 → 0.17.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/README.md +4 -4
- package/dist/cli.js +108 -10
- package/dist/index.d.ts +240 -17
- package/dist/index.js +7 -2
- package/dist/{render-verdict-Bj2tGqBI.js → render-verdict-Bx9Lw7gO.js} +1246 -990
- package/mods/auto-mode/hooks/build-prompt-context.ts +18 -8
- package/mods/auto-mode/hooks/register.ts +6 -13
- package/mods/auto-mode/hooks/types.ts +19 -1
- package/package.json +28 -13
- package/policy/classifier.md +29 -21
- package/policy/decision.md +9 -5
- package/policy/judge.md +57 -0
- package/policy/rules.md +119 -142
package/README.md
CHANGED
|
@@ -58,10 +58,10 @@ assessment.
|
|
|
58
58
|
|
|
59
59
|
## Decisions and failures
|
|
60
60
|
|
|
61
|
-
Jev evaluates each named rule with a typed choice. auto-mode
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
Jev evaluates each named rule with a typed choice between allow and block. auto-mode allows the
|
|
62
|
+
action unless some rule's block probability reaches the threshold, 0.2 by default. Every denial
|
|
63
|
+
names the rule with the highest block probability, the harm, and what clears it, and the agent
|
|
64
|
+
continues on another path. The threshold is configurable and needs evaluation against your actions.
|
|
65
65
|
|
|
66
66
|
A missing key, failed API call, malformed response, or oversized request follows
|
|
67
67
|
`decision.onFailure`. The default `defer` writes no verdict and keeps the prompt; `deny` fails
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as
|
|
3
|
-
import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
2
|
+
import { C as resolveStateDir, D as DEFAULT_SCOPE_SOURCES, E as DEFAULT_DENIAL_BUDGET, F as string, N as number, O as loadConfig, P as strictObject, a as loadSessionScope, b as splitCommandWords, f as loadRepositoryContext, g as loadPolicy, i as resolveSessionScopePath, j as readHostEnvironment, n as parseActionRequest, o as EMPTY_SESSION_SCOPE, p as loadCheckoutRemotes, r as classifyAction, t as renderVerdict, v as toRepositorySlug, w as findCheckout, y as splitShellCommand } from "./render-verdict-Bx9Lw7gO.js";
|
|
3
|
+
import { appendFile, chmod, lstat, mkdir, open, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, normalize, resolve } from "node:path";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { constants } from "node:fs";
|
|
6
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
8
|
import { text } from "node:stream/consumers";
|
|
8
9
|
import { parseArgs } from "node:util";
|
|
@@ -16,11 +17,13 @@ const DESCRIPTIVE_FIELDS = { Bash: [
|
|
|
16
17
|
function buildRetryKey(request) {
|
|
17
18
|
const ignored = Object.hasOwn(DESCRIPTIVE_FIELDS, request.toolName) ? DESCRIPTIVE_FIELDS[request.toolName] ?? [] : [];
|
|
18
19
|
const input = Object.fromEntries(Object.entries(request.toolInput).filter(([key]) => !ignored.includes(key)));
|
|
20
|
+
const message = request.decisionContext?.lastDirectUserMessage ?? null;
|
|
19
21
|
const canonical = JSON.stringify(sortKeys({
|
|
20
22
|
cwd: request.cwd,
|
|
21
23
|
toolName: request.toolName,
|
|
22
24
|
toolInput: input,
|
|
23
|
-
lastDirectUserMessage:
|
|
25
|
+
lastDirectUserMessage: message?.text ?? null,
|
|
26
|
+
...message?.freshness === "stale" ? { lastDirectUserMessageFreshness: "stale" } : {}
|
|
24
27
|
}));
|
|
25
28
|
return createHash("sha256").update(canonical).digest("hex");
|
|
26
29
|
}
|
|
@@ -136,6 +139,82 @@ async function writeDenialState(path, state) {
|
|
|
136
139
|
await rename(staged, path);
|
|
137
140
|
}
|
|
138
141
|
//#endregion
|
|
142
|
+
//#region src/capture/resolve-capture-dir.ts
|
|
143
|
+
function resolveCaptureDir(capture, host) {
|
|
144
|
+
return capture.dir ?? join(resolveStateDir(host), "captures");
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/capture/find-enclosing-work-tree.ts
|
|
148
|
+
async function findEnclosingWorkTree(path) {
|
|
149
|
+
const real = await resolveExistingPrefix(resolve(path));
|
|
150
|
+
for (let directory = real;; directory = dirname(directory)) {
|
|
151
|
+
if (await hasGitEntry(directory)) return directory;
|
|
152
|
+
if (dirname(directory) === directory) return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function resolveExistingPrefix(path) {
|
|
156
|
+
const missing = [];
|
|
157
|
+
for (let current = path;; current = dirname(current)) {
|
|
158
|
+
const real = await realpath(current).catch(() => null);
|
|
159
|
+
if (real !== null) return join(real, ...missing.toReversed());
|
|
160
|
+
if (dirname(current) === current) return path;
|
|
161
|
+
missing.push(basename(current));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR"]);
|
|
165
|
+
async function hasGitEntry(directory) {
|
|
166
|
+
try {
|
|
167
|
+
await lstat(join(directory, ".git"));
|
|
168
|
+
return true;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return !(error instanceof Error && "code" in error && ABSENT_CODES.has(String(error.code)));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/capture/try-write-request-capture.ts
|
|
175
|
+
async function tryWriteRequestCapture(dir, record) {
|
|
176
|
+
if (!isAbsolute(dir)) return {
|
|
177
|
+
kind: "refused",
|
|
178
|
+
reason: `the capture dir ${dir} is not an absolute path`
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
const before = await findEnclosingWorkTree(dir);
|
|
182
|
+
if (before !== null) return {
|
|
183
|
+
kind: "refused",
|
|
184
|
+
reason: `the capture dir is inside the git work tree ${before}`
|
|
185
|
+
};
|
|
186
|
+
await mkdir(dir, {
|
|
187
|
+
recursive: true,
|
|
188
|
+
mode: 448
|
|
189
|
+
});
|
|
190
|
+
const real = await realpath(dir);
|
|
191
|
+
const after = await findEnclosingWorkTree(real);
|
|
192
|
+
if (after !== null) return {
|
|
193
|
+
kind: "refused",
|
|
194
|
+
reason: `the capture dir is inside the git work tree ${after}`
|
|
195
|
+
};
|
|
196
|
+
if (((await stat(real)).mode & 63) !== 0) await chmod(real, 448);
|
|
197
|
+
await writeOwnerFile(join(real, ".gitignore"), "*\n", constants.O_TRUNC);
|
|
198
|
+
const path = join(real, `requests-${record.time.slice(0, 10)}.jsonl`);
|
|
199
|
+
await writeOwnerFile(path, `${JSON.stringify(record)}\n`, constants.O_APPEND);
|
|
200
|
+
return {
|
|
201
|
+
kind: "written",
|
|
202
|
+
path
|
|
203
|
+
};
|
|
204
|
+
} catch {
|
|
205
|
+
return { kind: "failed" };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function writeOwnerFile(path, text, flag) {
|
|
209
|
+
const handle = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW | flag, 384);
|
|
210
|
+
try {
|
|
211
|
+
if (((await handle.stat()).mode & 63) !== 0) await handle.chmod(384);
|
|
212
|
+
await handle.writeFile(text);
|
|
213
|
+
} finally {
|
|
214
|
+
await handle.close();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
139
218
|
//#region src/config/build-jev-only-config.ts
|
|
140
219
|
function buildJevOnlyConfig(config) {
|
|
141
220
|
if (config.provider.protocol !== "system-one") return null;
|
|
@@ -153,7 +232,7 @@ async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
|
153
232
|
const path = host.env["AUTO_MODE_DIAGNOSTICS_PATH"] ?? join(resolveStateDir(host), "actions.jsonl");
|
|
154
233
|
if (path === "") return;
|
|
155
234
|
const record = {
|
|
156
|
-
schemaVersion:
|
|
235
|
+
schemaVersion: 4,
|
|
157
236
|
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
158
237
|
invocationID: entry.invocationID,
|
|
159
238
|
sessionHash: toHash(payload.sessionID),
|
|
@@ -163,7 +242,8 @@ async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
|
163
242
|
decidingStage: entry.decidingStage ?? null,
|
|
164
243
|
denials: entry.denials ?? null,
|
|
165
244
|
escalation: entry.escalation ?? false,
|
|
166
|
-
diagnostics: entry.diagnostics ?? null
|
|
245
|
+
diagnostics: entry.diagnostics ?? null,
|
|
246
|
+
judge: entry.judge ?? null
|
|
167
247
|
};
|
|
168
248
|
try {
|
|
169
249
|
await mkdir(dirname(path), {
|
|
@@ -196,7 +276,8 @@ function parseScopeRecordRequest(body) {
|
|
|
196
276
|
//#endregion
|
|
197
277
|
//#region src/scope/read-pull-request.ts
|
|
198
278
|
const LOOKUP_TIMEOUT_MS = 5e3;
|
|
199
|
-
function readPullRequest(repository, number) {
|
|
279
|
+
function readPullRequest(repository, number, options = {}) {
|
|
280
|
+
const [file, ...prefix] = options.command ?? ["gh"];
|
|
200
281
|
const args = [
|
|
201
282
|
"pr",
|
|
202
283
|
"view",
|
|
@@ -207,7 +288,8 @@ function readPullRequest(repository, number) {
|
|
|
207
288
|
"headRefName,createdAt"
|
|
208
289
|
];
|
|
209
290
|
return new Promise((resolve) => {
|
|
210
|
-
const child = spawn(
|
|
291
|
+
const child = spawn(file, [
|
|
292
|
+
...prefix,
|
|
211
293
|
...args,
|
|
212
294
|
"--jq",
|
|
213
295
|
"[.headRefName, .createdAt] | @tsv"
|
|
@@ -217,7 +299,7 @@ function readPullRequest(repository, number) {
|
|
|
217
299
|
"pipe",
|
|
218
300
|
"ignore"
|
|
219
301
|
],
|
|
220
|
-
timeout: LOOKUP_TIMEOUT_MS
|
|
302
|
+
timeout: options.timeoutMs ?? LOOKUP_TIMEOUT_MS
|
|
221
303
|
});
|
|
222
304
|
let output = "";
|
|
223
305
|
child.stdout.setEncoding("utf8");
|
|
@@ -615,8 +697,20 @@ async function run(io, explain, localOnly, jevOnly, deadline) {
|
|
|
615
697
|
session: plan.state.session
|
|
616
698
|
},
|
|
617
699
|
escalation: plan.escalation,
|
|
618
|
-
...outcome.diagnostics === void 0 ? {} : { diagnostics: outcome.diagnostics }
|
|
700
|
+
...outcome.diagnostics === void 0 ? {} : { diagnostics: outcome.diagnostics },
|
|
701
|
+
...outcome.judge === void 0 ? {} : { judge: outcome.judge }
|
|
619
702
|
}, host, io.stderr);
|
|
703
|
+
if (config.capture?.enabled === true) {
|
|
704
|
+
const captured = await tryWriteRequestCapture(resolveCaptureDir(config.capture, host), {
|
|
705
|
+
schemaVersion: 1,
|
|
706
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
707
|
+
request: body,
|
|
708
|
+
verdict: plan.verdict,
|
|
709
|
+
decidingStage: plan.escalation ? "budget" : outcome.decidingStage,
|
|
710
|
+
escalation: plan.escalation
|
|
711
|
+
});
|
|
712
|
+
printCaptureNote(io.stderr, captured);
|
|
713
|
+
}
|
|
620
714
|
const note = plan.escalation ? "denial budget exhausted; the user decides this action" : outcome.note;
|
|
621
715
|
return printNote(io.stderr, explain || outcome.unavailable === true, note);
|
|
622
716
|
} finally {
|
|
@@ -640,6 +734,10 @@ async function tryWriteDenialState(stderr, path, state) {
|
|
|
640
734
|
function printVerdict(stdout, verdict) {
|
|
641
735
|
stdout.write(renderVerdict(verdict));
|
|
642
736
|
}
|
|
737
|
+
function printCaptureNote(stderr, captured) {
|
|
738
|
+
if (captured.kind === "refused") stderr.write(`auto-mode: capture skipped: ${captured.reason}\n`);
|
|
739
|
+
if (captured.kind === "failed") stderr.write("auto-mode: capture unavailable\n");
|
|
740
|
+
}
|
|
643
741
|
async function runRecord(io) {
|
|
644
742
|
const raw = await io.stdin();
|
|
645
743
|
let body;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
//#region src/budget/types.d.ts
|
|
2
|
-
interface DenialBudget {
|
|
3
|
-
readonly consecutive: number;
|
|
4
|
-
readonly perSession: number;
|
|
5
|
-
}
|
|
6
|
-
//#endregion
|
|
7
1
|
//#region src/request/types.d.ts
|
|
8
2
|
interface UserTask {
|
|
9
3
|
readonly text: string;
|
|
10
4
|
readonly origin: 'composer' | 'bridge' | 'sdk';
|
|
11
5
|
}
|
|
6
|
+
interface DirectUserMessage extends UserTask {
|
|
7
|
+
readonly freshness?: 'stale' | undefined;
|
|
8
|
+
}
|
|
12
9
|
interface DecisionContext {
|
|
13
10
|
readonly agentID: string | null;
|
|
14
11
|
readonly originalUserTask: UserTask | null;
|
|
@@ -16,7 +13,7 @@ interface DecisionContext {
|
|
|
16
13
|
readonly text: string;
|
|
17
14
|
readonly origin: 'agent.spawn';
|
|
18
15
|
} | null;
|
|
19
|
-
readonly lastDirectUserMessage:
|
|
16
|
+
readonly lastDirectUserMessage: DirectUserMessage | null;
|
|
20
17
|
readonly omittedTaskContext: readonly {
|
|
21
18
|
readonly field: 'originalUserTask' | 'delegatedTask';
|
|
22
19
|
readonly reason: 'unavailable' | 'budget';
|
|
@@ -45,6 +42,51 @@ interface DecisionRule {
|
|
|
45
42
|
readonly source: 'shipped' | 'replacement' | 'configured';
|
|
46
43
|
readonly text: string;
|
|
47
44
|
}
|
|
45
|
+
type DecisionChoice = 'allow' | 'block';
|
|
46
|
+
type RecordedDecisionChoice = DecisionChoice | 'ask';
|
|
47
|
+
interface DecisionQuestion<Choice extends string = DecisionChoice> {
|
|
48
|
+
readonly type: 'choice';
|
|
49
|
+
readonly instructions: string;
|
|
50
|
+
readonly criteria: Readonly<Record<Choice, string>>;
|
|
51
|
+
}
|
|
52
|
+
interface DecisionInput<Choice extends string = DecisionChoice> {
|
|
53
|
+
readonly state: {
|
|
54
|
+
readonly policy: string;
|
|
55
|
+
readonly answerGuidance: string;
|
|
56
|
+
readonly rulesSource: 'shipped' | 'replacement';
|
|
57
|
+
readonly configuredRules: ClaudeRules;
|
|
58
|
+
readonly lastUserMessage: string | null;
|
|
59
|
+
readonly repositoryContext?: RepositoryContext;
|
|
60
|
+
readonly mcpServers?: readonly MCPServerFact[];
|
|
61
|
+
readonly taskContext?: DecisionContext;
|
|
62
|
+
readonly action: {
|
|
63
|
+
readonly tool: string;
|
|
64
|
+
readonly cwd: string;
|
|
65
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
readonly questions: Readonly<Record<string, DecisionQuestion<Choice>>>;
|
|
69
|
+
}
|
|
70
|
+
interface DecisionRequest extends DecisionInput {
|
|
71
|
+
readonly rules: Readonly<Record<string, DecisionRule>>;
|
|
72
|
+
}
|
|
73
|
+
interface RepositoryContext {
|
|
74
|
+
readonly cwd: string;
|
|
75
|
+
readonly branch: string | null;
|
|
76
|
+
readonly defaultBranch: string | null;
|
|
77
|
+
readonly remotes?: readonly RepositoryRemote[] | undefined;
|
|
78
|
+
readonly taskScope?: TaskScopeSummary | undefined;
|
|
79
|
+
}
|
|
80
|
+
interface MCPServerFact {
|
|
81
|
+
readonly name: string;
|
|
82
|
+
readonly scope: 'user' | 'local' | 'project';
|
|
83
|
+
readonly transport: 'stdio' | 'http' | 'sse' | 'ws';
|
|
84
|
+
readonly host: string | null;
|
|
85
|
+
}
|
|
86
|
+
interface RepositoryRemote {
|
|
87
|
+
readonly name: string;
|
|
88
|
+
readonly url: string;
|
|
89
|
+
}
|
|
48
90
|
interface TaskScopeSummary {
|
|
49
91
|
readonly worktrees: readonly string[];
|
|
50
92
|
readonly branches: readonly string[];
|
|
@@ -53,12 +95,18 @@ interface TaskScopeSummary {
|
|
|
53
95
|
readonly number: number;
|
|
54
96
|
}[];
|
|
55
97
|
}
|
|
56
|
-
interface DecisionAnswer {
|
|
98
|
+
interface DecisionAnswer<Choice extends string = DecisionChoice> {
|
|
57
99
|
readonly type: 'choice';
|
|
58
|
-
readonly choice:
|
|
59
|
-
readonly probabilities: Readonly<Record<
|
|
100
|
+
readonly choice: Choice;
|
|
101
|
+
readonly probabilities: Readonly<Record<Choice, number>>;
|
|
60
102
|
readonly confidence: number;
|
|
61
103
|
}
|
|
104
|
+
interface DecisionResult<Choice extends string = DecisionChoice> {
|
|
105
|
+
readonly model: string;
|
|
106
|
+
readonly answers: Readonly<Record<string, DecisionAnswer<Choice>>>;
|
|
107
|
+
readonly inputTokens: number;
|
|
108
|
+
readonly requestBytes: number;
|
|
109
|
+
}
|
|
62
110
|
type DecisionFailureReason = 'request-too-large' | 'aborted' | 'network' | 'http-status' | 'invalid-response';
|
|
63
111
|
interface DecisionDiagnostics {
|
|
64
112
|
readonly status: 'allow' | 'deny' | 'failure' | 'timeout' | 'cancelled';
|
|
@@ -68,14 +116,14 @@ interface DecisionDiagnostics {
|
|
|
68
116
|
readonly failureReason: DecisionFailureReason | null;
|
|
69
117
|
readonly requestBytes: number | null;
|
|
70
118
|
readonly elapsedMs: number;
|
|
71
|
-
readonly
|
|
119
|
+
readonly blockThreshold: number;
|
|
72
120
|
readonly contributors: readonly {
|
|
73
121
|
readonly rule: string;
|
|
74
122
|
readonly source: DecisionRule['source'];
|
|
75
123
|
readonly tier: DecisionRule['tier'];
|
|
76
124
|
readonly choice: DecisionAnswer['choice'];
|
|
77
125
|
readonly confidence: number;
|
|
78
|
-
readonly
|
|
126
|
+
readonly blockProbability: number;
|
|
79
127
|
}[];
|
|
80
128
|
}
|
|
81
129
|
//#endregion
|
|
@@ -88,7 +136,18 @@ interface HostEnvironment {
|
|
|
88
136
|
readonly home: string;
|
|
89
137
|
readonly scratchPaths?: readonly string[] | undefined;
|
|
90
138
|
}
|
|
139
|
+
interface ClaudeRules {
|
|
140
|
+
readonly environment: readonly string[];
|
|
141
|
+
readonly allow: readonly string[];
|
|
142
|
+
readonly soft_deny: readonly string[];
|
|
143
|
+
readonly hard_deny: readonly string[];
|
|
144
|
+
}
|
|
145
|
+
interface ClaudeSettings {
|
|
146
|
+
readonly rules: ClaudeRules;
|
|
147
|
+
readonly userSettings: unknown;
|
|
148
|
+
}
|
|
91
149
|
interface EvaluationOptions {
|
|
150
|
+
readonly claudeSettings?: ClaudeSettings | undefined;
|
|
92
151
|
readonly deadlineAt?: number | undefined;
|
|
93
152
|
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
94
153
|
readonly taskScope?: TaskScopeSummary | undefined;
|
|
@@ -97,12 +156,79 @@ interface EvaluationOptions {
|
|
|
97
156
|
readonly timeout?: ((ms: number) => AbortSignal) | undefined;
|
|
98
157
|
}
|
|
99
158
|
//#endregion
|
|
159
|
+
//#region src/containment/collect-scope-findings.d.ts
|
|
160
|
+
interface ScopeRemote {
|
|
161
|
+
readonly name: string;
|
|
162
|
+
readonly url: string;
|
|
163
|
+
}
|
|
164
|
+
interface OwnedPullRequest {
|
|
165
|
+
readonly number: number;
|
|
166
|
+
readonly repository: string;
|
|
167
|
+
}
|
|
168
|
+
interface OwnedScope {
|
|
169
|
+
readonly home: string;
|
|
170
|
+
readonly worktrees: readonly string[];
|
|
171
|
+
readonly branches: readonly string[];
|
|
172
|
+
readonly currentBranch: string | null;
|
|
173
|
+
readonly remotes: readonly ScopeRemote[];
|
|
174
|
+
readonly pullRequests: readonly OwnedPullRequest[];
|
|
175
|
+
readonly pathGlobs: readonly string[];
|
|
176
|
+
}
|
|
177
|
+
interface ScopeFinding {
|
|
178
|
+
readonly kind: 'path' | 'branch' | 'remote-write' | 'credential' | 'prune';
|
|
179
|
+
readonly target: string;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/scope/find-checkout.d.ts
|
|
183
|
+
interface Checkout {
|
|
184
|
+
readonly worktree: string;
|
|
185
|
+
readonly commonDir: string;
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/bypass/classify-edit.d.ts
|
|
189
|
+
type EditClassification = {
|
|
190
|
+
readonly kind: 'bypass';
|
|
191
|
+
readonly target: string;
|
|
192
|
+
} | {
|
|
193
|
+
readonly kind: 'jev';
|
|
194
|
+
readonly reason: string;
|
|
195
|
+
};
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/bypass/try-classify-edit.d.ts
|
|
198
|
+
interface EditFileReader {
|
|
199
|
+
readonly resolveEditTarget: (path: string) => Promise<string | null>;
|
|
200
|
+
readonly findCheckout: (directory: string, env: HostEnvironment['env']) => Promise<Checkout | null>;
|
|
201
|
+
readonly readFile: (path: string) => Promise<string>;
|
|
202
|
+
}
|
|
203
|
+
export declare function tryClassifyEdit(request: Readonly<ActionRequest>, scope: Readonly<OwnedScope>, host: Readonly<HostEnvironment>, files?: Readonly<EditFileReader>): Promise<EditClassification | null>;
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/budget/types.d.ts
|
|
206
|
+
interface DenialBudget {
|
|
207
|
+
readonly consecutive: number;
|
|
208
|
+
readonly perSession: number;
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/judge/types.d.ts
|
|
212
|
+
type OverturnBasis = 'consent' | 'misread';
|
|
213
|
+
type JudgeOverturns = 'consent' | 'any';
|
|
214
|
+
type JudgeStatus = 'confirmed' | 'overturned' | 'held' | 'failed';
|
|
215
|
+
type JudgeFailure = 'credential' | 'policy' | 'request' | 'timeout' | 'unreadable';
|
|
216
|
+
interface JudgeDiagnostics {
|
|
217
|
+
readonly status: JudgeStatus;
|
|
218
|
+
readonly failureReason: JudgeFailure | null;
|
|
219
|
+
readonly overturnBasis: OverturnBasis | null;
|
|
220
|
+
readonly model: string;
|
|
221
|
+
readonly rule: string;
|
|
222
|
+
readonly tier: 'hard' | 'soft';
|
|
223
|
+
readonly elapsedMs: number;
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
100
226
|
//#region src/config/presets.d.ts
|
|
101
227
|
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
102
228
|
//#endregion
|
|
103
229
|
//#region src/config/config.d.ts
|
|
104
230
|
interface ProviderConfig {
|
|
105
|
-
readonly protocol?: 'messages' | 'system-one';
|
|
231
|
+
readonly protocol?: 'messages' | 'system-one' | 'claude-code';
|
|
106
232
|
readonly baseURL: string;
|
|
107
233
|
readonly model: string;
|
|
108
234
|
readonly apiKeyEnv?: string | undefined;
|
|
@@ -117,16 +243,22 @@ interface ScopeSource {
|
|
|
117
243
|
}
|
|
118
244
|
type ScopeSourceKind = (typeof SCOPE_SOURCE_KINDS)[number];
|
|
119
245
|
declare const SCOPE_SOURCE_KINDS: readonly ['cwd', 'session', 'globs', 'atc'];
|
|
246
|
+
interface CaptureConfig {
|
|
247
|
+
readonly enabled: boolean;
|
|
248
|
+
readonly dir?: string | undefined;
|
|
249
|
+
}
|
|
120
250
|
interface Config {
|
|
121
251
|
readonly provider: ProviderConfig;
|
|
122
252
|
readonly judge?: ProviderConfig | null;
|
|
253
|
+
readonly judgeOverturns?: JudgeOverturns;
|
|
123
254
|
readonly scopeSources?: Readonly<Record<string, ScopeSource>>;
|
|
124
255
|
readonly classifierPath?: string | undefined;
|
|
125
256
|
readonly rulesPath?: string | undefined;
|
|
126
257
|
readonly onFailure: 'defer' | 'deny';
|
|
127
258
|
readonly claudeSettingsPath?: string | null | undefined;
|
|
128
|
-
readonly
|
|
259
|
+
readonly blockThreshold?: number | undefined;
|
|
129
260
|
readonly denialBudget?: DenialBudget;
|
|
261
|
+
readonly capture?: CaptureConfig;
|
|
130
262
|
readonly warnings?: readonly string[];
|
|
131
263
|
}
|
|
132
264
|
export declare const DEFAULT_CONFIG: Config;
|
|
@@ -135,7 +267,7 @@ export declare function loadConfig(configPath?: string, host?: Readonly<HostEnvi
|
|
|
135
267
|
export declare function resolveApiKey(provider: ProviderConfig, options?: EvaluationOptions): Promise<string | null>;
|
|
136
268
|
//#endregion
|
|
137
269
|
//#region src/classify-action.d.ts
|
|
138
|
-
type DecidingStage = 'local' | 'containment' | 'bypass' | 'jev' | 'messages' | 'retry';
|
|
270
|
+
type DecidingStage = 'local' | 'containment' | 'bypass' | 'jev' | 'judge' | 'messages' | 'retry';
|
|
139
271
|
interface ActionOutcome {
|
|
140
272
|
readonly verdict: Verdict | null;
|
|
141
273
|
readonly decidingStage: DecidingStage;
|
|
@@ -143,6 +275,7 @@ interface ActionOutcome {
|
|
|
143
275
|
readonly status: 'allow' | 'skipped' | 'failure' | DecisionDiagnostics['status'];
|
|
144
276
|
readonly unavailable?: boolean;
|
|
145
277
|
readonly diagnostics?: DecisionDiagnostics;
|
|
278
|
+
readonly judge?: JudgeDiagnostics;
|
|
146
279
|
}
|
|
147
280
|
interface ClassifyOptions extends EvaluationOptions {
|
|
148
281
|
readonly localOnly?: boolean;
|
|
@@ -150,24 +283,70 @@ interface ClassifyOptions extends EvaluationOptions {
|
|
|
150
283
|
}
|
|
151
284
|
export declare function classifyAction(request: ActionRequest, config: Config, options?: ClassifyOptions): Promise<ActionOutcome>;
|
|
152
285
|
//#endregion
|
|
286
|
+
//#region src/config/load-claude-rules.d.ts
|
|
287
|
+
export declare function loadClaudeRules(path: string | null | undefined, host: Readonly<HostEnvironment>): Promise<ClaudeRules>;
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/containment/check-containment.d.ts
|
|
290
|
+
interface ContainmentDeny {
|
|
291
|
+
readonly rule: string;
|
|
292
|
+
readonly reason: string;
|
|
293
|
+
readonly findings: readonly ScopeFinding[];
|
|
294
|
+
}
|
|
295
|
+
export declare function checkContainment(request: Readonly<ActionRequest>, scope: Readonly<OwnedScope>, scratchPaths?: readonly string[]): ContainmentDeny | null;
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/model/build-decision-request.d.ts
|
|
298
|
+
export declare function buildDecisionRequest(payload: ActionRequest, policy: string, configuredRules: ClaudeRules, lastUserMessage: string | null, rulesSource?: 'shipped' | 'replacement', repositoryContext?: RepositoryContext | null, mcpServers?: readonly MCPServerFact[]): DecisionRequest;
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/policy/build-deny-reason.d.ts
|
|
301
|
+
type DenyBasis = 'matched' | 'unresolved';
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/model/find-denied-rule.d.ts
|
|
304
|
+
interface DeniedRule {
|
|
305
|
+
readonly rule: DecisionRule;
|
|
306
|
+
readonly basis: DenyBasis;
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/judge/classify-with-judge.d.ts
|
|
310
|
+
interface JudgeEvidence extends DeniedRule {
|
|
311
|
+
readonly repositoryContext: RepositoryContext | null;
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
153
314
|
//#region src/model/classify-with-model.d.ts
|
|
154
315
|
interface ModelOutcome {
|
|
155
316
|
readonly verdict: Verdict | null;
|
|
156
317
|
readonly note: string;
|
|
157
318
|
readonly unavailable?: boolean;
|
|
158
319
|
readonly diagnostics?: DecisionDiagnostics;
|
|
320
|
+
readonly review?: JudgeEvidence;
|
|
159
321
|
}
|
|
160
322
|
export declare function classifyWithModel(payload: ActionRequest, config: Config, options?: EvaluationOptions): Promise<ModelOutcome>;
|
|
161
323
|
//#endregion
|
|
324
|
+
//#region src/model/decision-request-error.d.ts
|
|
325
|
+
export declare class DecisionRequestError extends Error {
|
|
326
|
+
readonly reason: DecisionFailureReason;
|
|
327
|
+
readonly requestBytes: number;
|
|
328
|
+
constructor(reason: DecisionFailureReason, requestBytes: number, message: string, options?: Readonly<ErrorOptions>);
|
|
329
|
+
}
|
|
330
|
+
//#endregion
|
|
162
331
|
//#region src/model/parse-verdict.d.ts
|
|
163
332
|
export declare function parseModelVerdict(text: string): Verdict;
|
|
164
333
|
//#endregion
|
|
334
|
+
//#region src/model/send-decision.d.ts
|
|
335
|
+
interface SendOptions {
|
|
336
|
+
readonly fetch?: (url: string, init: Readonly<RequestInit>) => Promise<Response>;
|
|
337
|
+
}
|
|
338
|
+
interface SendDecisionOptions<Choice extends string> extends SendOptions {
|
|
339
|
+
readonly choices: readonly [Choice, ...Choice[]];
|
|
340
|
+
}
|
|
341
|
+
export declare function sendDecision(provider: ProviderConfig, apiKey: string, request: DecisionInput, signal: Readonly<AbortSignal>, options?: SendOptions): Promise<DecisionResult>;
|
|
342
|
+
export declare function sendDecision<Choice extends string>(provider: ProviderConfig, apiKey: string, request: DecisionInput<Choice>, signal: Readonly<AbortSignal>, options: SendDecisionOptions<Choice>): Promise<DecisionResult<Choice>>;
|
|
343
|
+
//#endregion
|
|
165
344
|
//#region src/policy/load-policy.d.ts
|
|
166
345
|
interface PolicyPaths {
|
|
167
346
|
readonly classifierPath?: string | undefined;
|
|
168
347
|
readonly rulesPath?: string | undefined;
|
|
169
348
|
}
|
|
170
|
-
export declare function loadPolicy(paths?: PolicyPaths, framework?: 'classifier.md' | 'decision.md'): Promise<string>;
|
|
349
|
+
export declare function loadPolicy(paths?: PolicyPaths, framework?: 'classifier.md' | 'decision.md' | 'judge.md'): Promise<string>;
|
|
171
350
|
//#endregion
|
|
172
351
|
//#region src/request/parse-action-request.d.ts
|
|
173
352
|
export declare function parseActionRequest(body: unknown): ActionRequest | null;
|
|
@@ -184,4 +363,48 @@ type LocalVerdict = {
|
|
|
184
363
|
};
|
|
185
364
|
export declare function classifyLocally(payload: ActionRequest): LocalVerdict;
|
|
186
365
|
//#endregion
|
|
187
|
-
|
|
366
|
+
//#region src/scope/types.d.ts
|
|
367
|
+
interface ScopePullRequest {
|
|
368
|
+
readonly number: number;
|
|
369
|
+
readonly head: string;
|
|
370
|
+
readonly repository: string;
|
|
371
|
+
}
|
|
372
|
+
interface ScopeFacts {
|
|
373
|
+
readonly worktrees: readonly string[];
|
|
374
|
+
readonly branches: readonly string[];
|
|
375
|
+
readonly pullRequests: readonly ScopePullRequest[];
|
|
376
|
+
readonly pathGlobs: readonly string[];
|
|
377
|
+
}
|
|
378
|
+
type ScopeEvent = {
|
|
379
|
+
readonly kind: 'worktree';
|
|
380
|
+
readonly path: string;
|
|
381
|
+
} | {
|
|
382
|
+
readonly kind: 'branch';
|
|
383
|
+
readonly name: string;
|
|
384
|
+
readonly directory: string;
|
|
385
|
+
} | {
|
|
386
|
+
readonly kind: 'pull-request';
|
|
387
|
+
readonly directory: string;
|
|
388
|
+
readonly head: string | null;
|
|
389
|
+
};
|
|
390
|
+
interface SessionBranch {
|
|
391
|
+
readonly name: string;
|
|
392
|
+
readonly commonDir: string;
|
|
393
|
+
}
|
|
394
|
+
interface SessionScope {
|
|
395
|
+
readonly worktrees: readonly string[];
|
|
396
|
+
readonly branches: readonly SessionBranch[];
|
|
397
|
+
readonly pullRequests: readonly ScopePullRequest[];
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/scope/build-task-scope.d.ts
|
|
401
|
+
interface TaskScopeInput {
|
|
402
|
+
readonly home: string;
|
|
403
|
+
readonly currentBranch: string | null;
|
|
404
|
+
readonly defaultBranch: string | null;
|
|
405
|
+
readonly remotes: readonly ScopeRemote[];
|
|
406
|
+
readonly facts: readonly ScopeFacts[];
|
|
407
|
+
}
|
|
408
|
+
export declare function buildTaskScope(input: Readonly<TaskScopeInput>): OwnedScope;
|
|
409
|
+
//#endregion
|
|
410
|
+
export type { ActionOutcome, ActionRequest, ClassifyOptions, ClaudeRules, Config, ContainmentDeny, DecisionContext, DecisionRequest, DecisionResult, DecisionRule, EditClassification, LocalVerdict, ModelOutcome, OwnedScope, PolicyPaths, ProviderConfig, RecordedDecisionChoice, RepositoryContext, ScopeEvent, ScopeSource, SessionScope, Verdict };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { A as resolveConfigPath, M as PRESETS, O as loadConfig, S as tryClassifyEdit, T as DEFAULT_CONFIG, _ as checkContainment, c as classifyLocally, d as sendDecision, g as loadPolicy, h as buildDecisionRequest, k as resolveApiKey, l as classifyWithModel, m as DecisionRequestError, n as parseActionRequest, r as classifyAction, s as buildTaskScope, t as renderVerdict, u as parseModelVerdict, x as loadClaudeSettings } from "./render-verdict-Bx9Lw7gO.js";
|
|
2
|
+
//#region src/config/load-claude-rules.ts
|
|
3
|
+
async function loadClaudeRules(path, host) {
|
|
4
|
+
return (await loadClaudeSettings(path, host)).rules;
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { DEFAULT_CONFIG, DecisionRequestError, PRESETS, buildDecisionRequest, buildTaskScope, checkContainment, classifyAction, classifyLocally, classifyWithModel, loadClaudeRules, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath, sendDecision, tryClassifyEdit };
|