auto-mode 0.15.0 → 0.16.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 +6 -4
- package/dist/index.d.ts +196 -13
- package/dist/index.js +7 -2
- package/dist/{render-verdict-Bj2tGqBI.js → render-verdict-u3_ftSEp.js} +248 -291
- package/mods/auto-mode/hooks/types.ts +14 -0
- package/package.json +25 -12
- package/policy/classifier.md +29 -21
- package/policy/decision.md +9 -5
- 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,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as
|
|
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-u3_ftSEp.js";
|
|
3
3
|
import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
@@ -196,7 +196,8 @@ function parseScopeRecordRequest(body) {
|
|
|
196
196
|
//#endregion
|
|
197
197
|
//#region src/scope/read-pull-request.ts
|
|
198
198
|
const LOOKUP_TIMEOUT_MS = 5e3;
|
|
199
|
-
function readPullRequest(repository, number) {
|
|
199
|
+
function readPullRequest(repository, number, options = {}) {
|
|
200
|
+
const [file, ...prefix] = options.command ?? ["gh"];
|
|
200
201
|
const args = [
|
|
201
202
|
"pr",
|
|
202
203
|
"view",
|
|
@@ -207,7 +208,8 @@ function readPullRequest(repository, number) {
|
|
|
207
208
|
"headRefName,createdAt"
|
|
208
209
|
];
|
|
209
210
|
return new Promise((resolve) => {
|
|
210
|
-
const child = spawn(
|
|
211
|
+
const child = spawn(file, [
|
|
212
|
+
...prefix,
|
|
211
213
|
...args,
|
|
212
214
|
"--jq",
|
|
213
215
|
"[.headRefName, .createdAt] | @tsv"
|
|
@@ -217,7 +219,7 @@ function readPullRequest(repository, number) {
|
|
|
217
219
|
"pipe",
|
|
218
220
|
"ignore"
|
|
219
221
|
],
|
|
220
|
-
timeout: LOOKUP_TIMEOUT_MS
|
|
222
|
+
timeout: options.timeoutMs ?? LOOKUP_TIMEOUT_MS
|
|
221
223
|
});
|
|
222
224
|
let output = "";
|
|
223
225
|
child.stdout.setEncoding("utf8");
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
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;
|
|
@@ -45,6 +39,51 @@ interface DecisionRule {
|
|
|
45
39
|
readonly source: 'shipped' | 'replacement' | 'configured';
|
|
46
40
|
readonly text: string;
|
|
47
41
|
}
|
|
42
|
+
type DecisionChoice = 'allow' | 'block';
|
|
43
|
+
type RecordedDecisionChoice = DecisionChoice | 'ask';
|
|
44
|
+
interface DecisionQuestion<Choice extends string = DecisionChoice> {
|
|
45
|
+
readonly type: 'choice';
|
|
46
|
+
readonly instructions: string;
|
|
47
|
+
readonly criteria: Readonly<Record<Choice, string>>;
|
|
48
|
+
}
|
|
49
|
+
interface DecisionInput<Choice extends string = DecisionChoice> {
|
|
50
|
+
readonly state: {
|
|
51
|
+
readonly policy: string;
|
|
52
|
+
readonly answerGuidance: string;
|
|
53
|
+
readonly rulesSource: 'shipped' | 'replacement';
|
|
54
|
+
readonly configuredRules: ClaudeRules;
|
|
55
|
+
readonly lastUserMessage: string | null;
|
|
56
|
+
readonly repositoryContext?: RepositoryContext;
|
|
57
|
+
readonly mcpServers?: readonly MCPServerFact[];
|
|
58
|
+
readonly taskContext?: DecisionContext;
|
|
59
|
+
readonly action: {
|
|
60
|
+
readonly tool: string;
|
|
61
|
+
readonly cwd: string;
|
|
62
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
readonly questions: Readonly<Record<string, DecisionQuestion<Choice>>>;
|
|
66
|
+
}
|
|
67
|
+
interface DecisionRequest extends DecisionInput {
|
|
68
|
+
readonly rules: Readonly<Record<string, DecisionRule>>;
|
|
69
|
+
}
|
|
70
|
+
interface RepositoryContext {
|
|
71
|
+
readonly cwd: string;
|
|
72
|
+
readonly branch: string | null;
|
|
73
|
+
readonly defaultBranch: string | null;
|
|
74
|
+
readonly remotes?: readonly RepositoryRemote[] | undefined;
|
|
75
|
+
readonly taskScope?: TaskScopeSummary | undefined;
|
|
76
|
+
}
|
|
77
|
+
interface MCPServerFact {
|
|
78
|
+
readonly name: string;
|
|
79
|
+
readonly scope: 'user' | 'local' | 'project';
|
|
80
|
+
readonly transport: 'stdio' | 'http' | 'sse' | 'ws';
|
|
81
|
+
readonly host: string | null;
|
|
82
|
+
}
|
|
83
|
+
interface RepositoryRemote {
|
|
84
|
+
readonly name: string;
|
|
85
|
+
readonly url: string;
|
|
86
|
+
}
|
|
48
87
|
interface TaskScopeSummary {
|
|
49
88
|
readonly worktrees: readonly string[];
|
|
50
89
|
readonly branches: readonly string[];
|
|
@@ -53,12 +92,18 @@ interface TaskScopeSummary {
|
|
|
53
92
|
readonly number: number;
|
|
54
93
|
}[];
|
|
55
94
|
}
|
|
56
|
-
interface DecisionAnswer {
|
|
95
|
+
interface DecisionAnswer<Choice extends string = DecisionChoice> {
|
|
57
96
|
readonly type: 'choice';
|
|
58
|
-
readonly choice:
|
|
59
|
-
readonly probabilities: Readonly<Record<
|
|
97
|
+
readonly choice: Choice;
|
|
98
|
+
readonly probabilities: Readonly<Record<Choice, number>>;
|
|
60
99
|
readonly confidence: number;
|
|
61
100
|
}
|
|
101
|
+
interface DecisionResult<Choice extends string = DecisionChoice> {
|
|
102
|
+
readonly model: string;
|
|
103
|
+
readonly answers: Readonly<Record<string, DecisionAnswer<Choice>>>;
|
|
104
|
+
readonly inputTokens: number;
|
|
105
|
+
readonly requestBytes: number;
|
|
106
|
+
}
|
|
62
107
|
type DecisionFailureReason = 'request-too-large' | 'aborted' | 'network' | 'http-status' | 'invalid-response';
|
|
63
108
|
interface DecisionDiagnostics {
|
|
64
109
|
readonly status: 'allow' | 'deny' | 'failure' | 'timeout' | 'cancelled';
|
|
@@ -68,14 +113,14 @@ interface DecisionDiagnostics {
|
|
|
68
113
|
readonly failureReason: DecisionFailureReason | null;
|
|
69
114
|
readonly requestBytes: number | null;
|
|
70
115
|
readonly elapsedMs: number;
|
|
71
|
-
readonly
|
|
116
|
+
readonly blockThreshold: number;
|
|
72
117
|
readonly contributors: readonly {
|
|
73
118
|
readonly rule: string;
|
|
74
119
|
readonly source: DecisionRule['source'];
|
|
75
120
|
readonly tier: DecisionRule['tier'];
|
|
76
121
|
readonly choice: DecisionAnswer['choice'];
|
|
77
122
|
readonly confidence: number;
|
|
78
|
-
readonly
|
|
123
|
+
readonly blockProbability: number;
|
|
79
124
|
}[];
|
|
80
125
|
}
|
|
81
126
|
//#endregion
|
|
@@ -88,7 +133,18 @@ interface HostEnvironment {
|
|
|
88
133
|
readonly home: string;
|
|
89
134
|
readonly scratchPaths?: readonly string[] | undefined;
|
|
90
135
|
}
|
|
136
|
+
interface ClaudeRules {
|
|
137
|
+
readonly environment: readonly string[];
|
|
138
|
+
readonly allow: readonly string[];
|
|
139
|
+
readonly soft_deny: readonly string[];
|
|
140
|
+
readonly hard_deny: readonly string[];
|
|
141
|
+
}
|
|
142
|
+
interface ClaudeSettings {
|
|
143
|
+
readonly rules: ClaudeRules;
|
|
144
|
+
readonly userSettings: unknown;
|
|
145
|
+
}
|
|
91
146
|
interface EvaluationOptions {
|
|
147
|
+
readonly claudeSettings?: ClaudeSettings | undefined;
|
|
92
148
|
readonly deadlineAt?: number | undefined;
|
|
93
149
|
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
94
150
|
readonly taskScope?: TaskScopeSummary | undefined;
|
|
@@ -97,6 +153,58 @@ interface EvaluationOptions {
|
|
|
97
153
|
readonly timeout?: ((ms: number) => AbortSignal) | undefined;
|
|
98
154
|
}
|
|
99
155
|
//#endregion
|
|
156
|
+
//#region src/containment/collect-scope-findings.d.ts
|
|
157
|
+
interface ScopeRemote {
|
|
158
|
+
readonly name: string;
|
|
159
|
+
readonly url: string;
|
|
160
|
+
}
|
|
161
|
+
interface OwnedPullRequest {
|
|
162
|
+
readonly number: number;
|
|
163
|
+
readonly repository: string;
|
|
164
|
+
}
|
|
165
|
+
interface OwnedScope {
|
|
166
|
+
readonly home: string;
|
|
167
|
+
readonly worktrees: readonly string[];
|
|
168
|
+
readonly branches: readonly string[];
|
|
169
|
+
readonly currentBranch: string | null;
|
|
170
|
+
readonly remotes: readonly ScopeRemote[];
|
|
171
|
+
readonly pullRequests: readonly OwnedPullRequest[];
|
|
172
|
+
readonly pathGlobs: readonly string[];
|
|
173
|
+
}
|
|
174
|
+
interface ScopeFinding {
|
|
175
|
+
readonly kind: 'path' | 'branch' | 'remote-write' | 'credential' | 'prune';
|
|
176
|
+
readonly target: string;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/scope/find-checkout.d.ts
|
|
180
|
+
interface Checkout {
|
|
181
|
+
readonly worktree: string;
|
|
182
|
+
readonly commonDir: string;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/bypass/classify-edit.d.ts
|
|
186
|
+
type EditClassification = {
|
|
187
|
+
readonly kind: 'bypass';
|
|
188
|
+
readonly target: string;
|
|
189
|
+
} | {
|
|
190
|
+
readonly kind: 'jev';
|
|
191
|
+
readonly reason: string;
|
|
192
|
+
};
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/bypass/try-classify-edit.d.ts
|
|
195
|
+
interface EditFileReader {
|
|
196
|
+
readonly resolveEditTarget: (path: string) => Promise<string | null>;
|
|
197
|
+
readonly findCheckout: (directory: string, env: HostEnvironment['env']) => Promise<Checkout | null>;
|
|
198
|
+
readonly readFile: (path: string) => Promise<string>;
|
|
199
|
+
}
|
|
200
|
+
export declare function tryClassifyEdit(request: Readonly<ActionRequest>, scope: Readonly<OwnedScope>, host: Readonly<HostEnvironment>, files?: Readonly<EditFileReader>): Promise<EditClassification | null>;
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/budget/types.d.ts
|
|
203
|
+
interface DenialBudget {
|
|
204
|
+
readonly consecutive: number;
|
|
205
|
+
readonly perSession: number;
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
100
208
|
//#region src/config/presets.d.ts
|
|
101
209
|
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
102
210
|
//#endregion
|
|
@@ -125,7 +233,7 @@ interface Config {
|
|
|
125
233
|
readonly rulesPath?: string | undefined;
|
|
126
234
|
readonly onFailure: 'defer' | 'deny';
|
|
127
235
|
readonly claudeSettingsPath?: string | null | undefined;
|
|
128
|
-
readonly
|
|
236
|
+
readonly blockThreshold?: number | undefined;
|
|
129
237
|
readonly denialBudget?: DenialBudget;
|
|
130
238
|
readonly warnings?: readonly string[];
|
|
131
239
|
}
|
|
@@ -150,6 +258,20 @@ interface ClassifyOptions extends EvaluationOptions {
|
|
|
150
258
|
}
|
|
151
259
|
export declare function classifyAction(request: ActionRequest, config: Config, options?: ClassifyOptions): Promise<ActionOutcome>;
|
|
152
260
|
//#endregion
|
|
261
|
+
//#region src/config/load-claude-rules.d.ts
|
|
262
|
+
export declare function loadClaudeRules(path: string | null | undefined, host: Readonly<HostEnvironment>): Promise<ClaudeRules>;
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/containment/check-containment.d.ts
|
|
265
|
+
interface ContainmentDeny {
|
|
266
|
+
readonly rule: string;
|
|
267
|
+
readonly reason: string;
|
|
268
|
+
readonly findings: readonly ScopeFinding[];
|
|
269
|
+
}
|
|
270
|
+
export declare function checkContainment(request: Readonly<ActionRequest>, scope: Readonly<OwnedScope>, scratchPaths?: readonly string[]): ContainmentDeny | null;
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/model/build-decision-request.d.ts
|
|
273
|
+
export declare function buildDecisionRequest(payload: ActionRequest, policy: string, configuredRules: ClaudeRules, lastUserMessage: string | null, rulesSource?: 'shipped' | 'replacement', repositoryContext?: RepositoryContext | null, mcpServers?: readonly MCPServerFact[]): DecisionRequest;
|
|
274
|
+
//#endregion
|
|
153
275
|
//#region src/model/classify-with-model.d.ts
|
|
154
276
|
interface ModelOutcome {
|
|
155
277
|
readonly verdict: Verdict | null;
|
|
@@ -159,9 +281,26 @@ interface ModelOutcome {
|
|
|
159
281
|
}
|
|
160
282
|
export declare function classifyWithModel(payload: ActionRequest, config: Config, options?: EvaluationOptions): Promise<ModelOutcome>;
|
|
161
283
|
//#endregion
|
|
284
|
+
//#region src/model/decision-request-error.d.ts
|
|
285
|
+
export declare class DecisionRequestError extends Error {
|
|
286
|
+
readonly reason: DecisionFailureReason;
|
|
287
|
+
readonly requestBytes: number;
|
|
288
|
+
constructor(reason: DecisionFailureReason, requestBytes: number, message: string, options?: Readonly<ErrorOptions>);
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
162
291
|
//#region src/model/parse-verdict.d.ts
|
|
163
292
|
export declare function parseModelVerdict(text: string): Verdict;
|
|
164
293
|
//#endregion
|
|
294
|
+
//#region src/model/send-decision.d.ts
|
|
295
|
+
interface SendOptions {
|
|
296
|
+
readonly fetch?: (url: string, init: Readonly<RequestInit>) => Promise<Response>;
|
|
297
|
+
}
|
|
298
|
+
interface SendDecisionOptions<Choice extends string> extends SendOptions {
|
|
299
|
+
readonly choices: readonly [Choice, ...Choice[]];
|
|
300
|
+
}
|
|
301
|
+
export declare function sendDecision(provider: ProviderConfig, apiKey: string, request: DecisionInput, signal: Readonly<AbortSignal>, options?: SendOptions): Promise<DecisionResult>;
|
|
302
|
+
export declare function sendDecision<Choice extends string>(provider: ProviderConfig, apiKey: string, request: DecisionInput<Choice>, signal: Readonly<AbortSignal>, options: SendDecisionOptions<Choice>): Promise<DecisionResult<Choice>>;
|
|
303
|
+
//#endregion
|
|
165
304
|
//#region src/policy/load-policy.d.ts
|
|
166
305
|
interface PolicyPaths {
|
|
167
306
|
readonly classifierPath?: string | undefined;
|
|
@@ -184,4 +323,48 @@ type LocalVerdict = {
|
|
|
184
323
|
};
|
|
185
324
|
export declare function classifyLocally(payload: ActionRequest): LocalVerdict;
|
|
186
325
|
//#endregion
|
|
187
|
-
|
|
326
|
+
//#region src/scope/types.d.ts
|
|
327
|
+
interface ScopePullRequest {
|
|
328
|
+
readonly number: number;
|
|
329
|
+
readonly head: string;
|
|
330
|
+
readonly repository: string;
|
|
331
|
+
}
|
|
332
|
+
interface ScopeFacts {
|
|
333
|
+
readonly worktrees: readonly string[];
|
|
334
|
+
readonly branches: readonly string[];
|
|
335
|
+
readonly pullRequests: readonly ScopePullRequest[];
|
|
336
|
+
readonly pathGlobs: readonly string[];
|
|
337
|
+
}
|
|
338
|
+
type ScopeEvent = {
|
|
339
|
+
readonly kind: 'worktree';
|
|
340
|
+
readonly path: string;
|
|
341
|
+
} | {
|
|
342
|
+
readonly kind: 'branch';
|
|
343
|
+
readonly name: string;
|
|
344
|
+
readonly directory: string;
|
|
345
|
+
} | {
|
|
346
|
+
readonly kind: 'pull-request';
|
|
347
|
+
readonly directory: string;
|
|
348
|
+
readonly head: string | null;
|
|
349
|
+
};
|
|
350
|
+
interface SessionBranch {
|
|
351
|
+
readonly name: string;
|
|
352
|
+
readonly commonDir: string;
|
|
353
|
+
}
|
|
354
|
+
interface SessionScope {
|
|
355
|
+
readonly worktrees: readonly string[];
|
|
356
|
+
readonly branches: readonly SessionBranch[];
|
|
357
|
+
readonly pullRequests: readonly ScopePullRequest[];
|
|
358
|
+
}
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/scope/build-task-scope.d.ts
|
|
361
|
+
interface TaskScopeInput {
|
|
362
|
+
readonly home: string;
|
|
363
|
+
readonly currentBranch: string | null;
|
|
364
|
+
readonly defaultBranch: string | null;
|
|
365
|
+
readonly remotes: readonly ScopeRemote[];
|
|
366
|
+
readonly facts: readonly ScopeFacts[];
|
|
367
|
+
}
|
|
368
|
+
export declare function buildTaskScope(input: Readonly<TaskScopeInput>): OwnedScope;
|
|
369
|
+
//#endregion
|
|
370
|
+
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-u3_ftSEp.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 };
|