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 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 returns a denial for a confident block,
62
- approval when every rule confidently allows the action, and a denial for uncertain decisions. Every
63
- denial names its rule, the harm, and what clears it, and the agent continues on another path. The
64
- confidence threshold is configurable and needs evaluation against your actions.
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 strictObject, E as number, O as string, _ as findCheckout, a as loadSessionScope, b as DEFAULT_SCOPE_SOURCES, d as loadCheckoutRemotes, f as loadPolicy, g as resolveStateDir, h as splitCommandWords, i as resolveSessionScopePath, m as splitShellCommand, n as parseActionRequest, o as EMPTY_SESSION_SCOPE, p as toRepositorySlug, r as classifyAction, t as renderVerdict, u as loadRepositoryContext, w as readHostEnvironment, x as loadConfig, y as DEFAULT_DENIAL_BUDGET } from "./render-verdict-Bj2tGqBI.js";
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: request.decisionContext?.lastDirectUserMessage?.text ?? null
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: 3,
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("gh", [
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: UserTask | null;
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: 'allow' | 'block' | 'ask';
59
- readonly probabilities: Readonly<Record<'allow' | 'block' | 'ask', number>>;
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 minConfidence: number;
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 probability: number;
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 minConfidence?: number | undefined;
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
- export type { ActionOutcome, ActionRequest, ClassifyOptions, Config, DecisionContext, LocalVerdict, ModelOutcome, PolicyPaths, ProviderConfig, ScopeSource, Verdict };
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 { C as resolveConfigPath, S as resolveApiKey, T as PRESETS, c as classifyWithModel, f as loadPolicy, l as parseModelVerdict, n as parseActionRequest, r as classifyAction, s as classifyLocally, t as renderVerdict, v as DEFAULT_CONFIG, x as loadConfig } from "./render-verdict-Bj2tGqBI.js";
2
- export { DEFAULT_CONFIG, PRESETS, classifyAction, classifyLocally, classifyWithModel, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath };
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 };