auto-mode 0.5.0 → 0.7.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 +15 -12
- package/dist/cli.js +51 -4
- package/dist/index.d.ts +15 -6
- package/dist/index.js +1 -1
- package/dist/{render-verdict-FdG6gahX.js → render-verdict-CAg3AKWH.js} +841 -432
- package/package.json +1 -1
- package/policy/denial.md +1 -0
package/README.md
CHANGED
|
@@ -31,14 +31,14 @@ Create the classifier configuration:
|
|
|
31
31
|
|
|
32
32
|
```json
|
|
33
33
|
{
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"classifiers": { "jev": { "apiKeyEnv": "TYPESAFE_API_KEY" } },
|
|
35
|
+
"decision": { "classifier": "jev" }
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Save it as `~/.config/auto-mode/config.json` and export your TypeSafe API key, or set
|
|
40
|
-
`
|
|
41
|
-
|
|
39
|
+
Save it as `~/.config/auto-mode/config.json` and export your TypeSafe API key, or set the entry's
|
|
40
|
+
`apiKeyCommand` to a command that prints it. [Configuration](./docs/guides/configuration.md) covers
|
|
41
|
+
the registries, credentials, and migrating an older file.
|
|
42
42
|
|
|
43
43
|
## Standing permissions
|
|
44
44
|
|
|
@@ -59,12 +59,14 @@ assessment.
|
|
|
59
59
|
## Decisions and failures
|
|
60
60
|
|
|
61
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
|
|
63
|
-
|
|
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.
|
|
64
65
|
|
|
65
|
-
A missing key, failed API call, malformed response, or oversized request follows
|
|
66
|
-
default `defer` writes no verdict and keeps the prompt; `deny` fails
|
|
67
|
-
diagnostic to stderr. auto-mode never truncates a Jev action to make it
|
|
66
|
+
A missing key, failed API call, malformed response, or oversized request follows
|
|
67
|
+
`decision.onFailure`. The default `defer` writes no verdict and keeps the prompt; `deny` fails
|
|
68
|
+
closed. Failures write a diagnostic to stderr. auto-mode never truncates a Jev action to make it
|
|
69
|
+
fit.
|
|
68
70
|
|
|
69
71
|
## Commands
|
|
70
72
|
|
|
@@ -75,10 +77,11 @@ diagnostic to stderr. auto-mode never truncates a Jev action to make it fit.
|
|
|
75
77
|
| `auto-mode run --local-only` | Skip the model tier |
|
|
76
78
|
| `auto-mode run --jev-only` | Require Jev with a 5-second API timeout |
|
|
77
79
|
| `auto-mode print-prompt` | Print the selected provider's base policy |
|
|
80
|
+
| `auto-mode config migrate` | Rewrite an older config file in place |
|
|
78
81
|
|
|
79
82
|
The mod evaluates only ask decisions and preserves existing allow and deny decisions. It runs the
|
|
80
|
-
CLI as a bounded child process with `--jev-only`. The `spark`, `claude`, and `
|
|
81
|
-
Messages API and serve library callers.
|
|
83
|
+
CLI as a bounded child process with `--jev-only`. The `spark`, `claude`, `glm`, and `messages`
|
|
84
|
+
classifier kinds use the Messages API and serve library callers.
|
|
82
85
|
|
|
83
86
|
## Library
|
|
84
87
|
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as parseActionRequest, r as classifyAction, s as loadPolicy, t as renderVerdict
|
|
3
|
-
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { d as resolveConfigPath, f as normalizeConfigFile, l as loadConfig, n as parseActionRequest, r as classifyAction, s as loadPolicy, t as renderVerdict } from "./render-verdict-CAg3AKWH.js";
|
|
3
|
+
import { appendFile, constants, copyFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -18,6 +18,36 @@ function buildJevOnlyConfig(config) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
//#endregion
|
|
21
|
+
//#region src/config/write-migrated-config.ts
|
|
22
|
+
async function writeMigratedConfig(path) {
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
raw = await readFile(path, "utf8");
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return `${path} does not exist; nothing to migrate`;
|
|
28
|
+
throw new Error(`${path} is unreadable`, { cause: error });
|
|
29
|
+
}
|
|
30
|
+
let json;
|
|
31
|
+
try {
|
|
32
|
+
json = JSON.parse(raw);
|
|
33
|
+
} catch {
|
|
34
|
+
throw new Error(`${path} is not valid JSON`);
|
|
35
|
+
}
|
|
36
|
+
const normalized = normalizeConfigFile(json, path);
|
|
37
|
+
if (!normalized.isLegacy) return `${path} already uses the current shape`;
|
|
38
|
+
const backup = `${path}.bak`;
|
|
39
|
+
const staged = `${path}.migrating`;
|
|
40
|
+
try {
|
|
41
|
+
await copyFile(path, backup, constants.COPYFILE_EXCL);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") throw new Error(`${backup} already exists; move it aside and run the migration again`, { cause: error });
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
await writeFile(staged, `${JSON.stringify(normalized.file, null, 2)}\n`, { mode: 384 });
|
|
47
|
+
await rename(staged, path);
|
|
48
|
+
return `rewrote ${path} in the current shape; the old file is at ${backup}`;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
21
51
|
//#region src/diagnostics/write-action-diagnostic.ts
|
|
22
52
|
async function writeActionDiagnostic(payload, entry) {
|
|
23
53
|
const stateHome = process.env["XDG_STATE_HOME"];
|
|
@@ -54,6 +84,7 @@ const USAGE = `auto-mode — a permission classifier for the auto-mode Claude Co
|
|
|
54
84
|
Usage:
|
|
55
85
|
auto-mode run Read an action request on stdin, write a verdict on stdout
|
|
56
86
|
auto-mode print-prompt Print the system prompt the classifier receives
|
|
87
|
+
auto-mode config migrate Rewrite the config file from the old keys into the current shape
|
|
57
88
|
|
|
58
89
|
Options:
|
|
59
90
|
--classifier <path> Use this framework file instead of the shipped one
|
|
@@ -92,6 +123,7 @@ async function run(explain, localOnly, jevOnly, deadline) {
|
|
|
92
123
|
});
|
|
93
124
|
return printNote(true, "configuration unreadable; no verdict");
|
|
94
125
|
}
|
|
126
|
+
printWarnings(loaded.warnings);
|
|
95
127
|
const config = jevOnly ? buildJevOnlyConfig(loaded) : loaded;
|
|
96
128
|
if (config === null) {
|
|
97
129
|
await writeActionDiagnostic(request, {
|
|
@@ -138,8 +170,10 @@ async function run(explain, localOnly, jevOnly, deadline) {
|
|
|
138
170
|
}
|
|
139
171
|
}
|
|
140
172
|
function writeVerdict(verdict) {
|
|
141
|
-
|
|
142
|
-
|
|
173
|
+
process.stdout.write(renderVerdict(verdict));
|
|
174
|
+
}
|
|
175
|
+
function printWarnings(warnings = []) {
|
|
176
|
+
for (const warning of warnings) process.stderr.write(`auto-mode: ${warning}\n`);
|
|
143
177
|
}
|
|
144
178
|
function printNote(explain, message) {
|
|
145
179
|
if (explain) process.stderr.write(`auto-mode: ${message}\n`);
|
|
@@ -175,6 +209,7 @@ async function main(argv) {
|
|
|
175
209
|
}
|
|
176
210
|
if (command === "print-prompt") {
|
|
177
211
|
const config = await loadConfig();
|
|
212
|
+
printWarnings(config.warnings);
|
|
178
213
|
const framework = config.provider.protocol === "system-one" ? "decision.md" : "classifier.md";
|
|
179
214
|
const prompt = await loadPolicy({
|
|
180
215
|
classifierPath: args.values.classifier ?? config.classifierPath,
|
|
@@ -183,9 +218,21 @@ async function main(argv) {
|
|
|
183
218
|
process.stdout.write(`${prompt}\n`);
|
|
184
219
|
return 0;
|
|
185
220
|
}
|
|
221
|
+
if (command === "config" && args.positionals[1] === "migrate") return runConfigMigration();
|
|
186
222
|
process.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
|
|
187
223
|
return 2;
|
|
188
224
|
}
|
|
225
|
+
async function runConfigMigration() {
|
|
226
|
+
const path = resolveConfigPath();
|
|
227
|
+
try {
|
|
228
|
+
const result = await writeMigratedConfig(path);
|
|
229
|
+
process.stdout.write(`${result}\n`);
|
|
230
|
+
return 0;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
process.stderr.write(`auto-mode: ${error instanceof Error ? error.message : "config migration failed"}\n`);
|
|
233
|
+
return 1;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
189
236
|
process.exitCode = await main(process.argv.slice(2));
|
|
190
237
|
//#endregion
|
|
191
238
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ interface EvaluationOptions {
|
|
|
4
4
|
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
5
5
|
}
|
|
6
6
|
//#endregion
|
|
7
|
+
//#region src/config/presets.d.ts
|
|
8
|
+
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
9
|
+
//#endregion
|
|
7
10
|
//#region src/config/config.d.ts
|
|
8
11
|
interface ProviderConfig {
|
|
9
12
|
readonly protocol?: 'messages' | 'system-one';
|
|
@@ -15,16 +18,24 @@ interface ProviderConfig {
|
|
|
15
18
|
readonly maxTokens: number;
|
|
16
19
|
readonly timeoutMs: number;
|
|
17
20
|
}
|
|
21
|
+
interface ScopeSource {
|
|
22
|
+
readonly kind: ScopeSourceKind;
|
|
23
|
+
readonly paths?: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
type ScopeSourceKind = (typeof SCOPE_SOURCE_KINDS)[number];
|
|
26
|
+
declare const SCOPE_SOURCE_KINDS: readonly ['cwd', 'session', 'globs', 'atc'];
|
|
18
27
|
interface Config {
|
|
19
28
|
readonly provider: ProviderConfig;
|
|
29
|
+
readonly judge?: ProviderConfig | null;
|
|
30
|
+
readonly scopeSources?: Readonly<Record<string, ScopeSource>>;
|
|
20
31
|
readonly classifierPath?: string | undefined;
|
|
21
32
|
readonly rulesPath?: string | undefined;
|
|
22
33
|
readonly onFailure: 'defer' | 'deny';
|
|
23
34
|
readonly claudeSettingsPath?: string | null | undefined;
|
|
24
35
|
readonly minConfidence?: number | undefined;
|
|
36
|
+
readonly warnings?: readonly string[];
|
|
25
37
|
}
|
|
26
38
|
export declare const DEFAULT_CONFIG: Config;
|
|
27
|
-
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
28
39
|
export declare function resolveConfigPath(): string;
|
|
29
40
|
export declare function loadConfig(path?: string): Promise<Config>;
|
|
30
41
|
export declare function resolveApiKey(provider: ProviderConfig, options?: EvaluationOptions): Promise<string | null>;
|
|
@@ -57,8 +68,6 @@ interface ActionRequest {
|
|
|
57
68
|
}
|
|
58
69
|
type Verdict = {
|
|
59
70
|
readonly kind: 'allow';
|
|
60
|
-
} | {
|
|
61
|
-
readonly kind: 'ask';
|
|
62
71
|
} | {
|
|
63
72
|
readonly kind: 'deny';
|
|
64
73
|
readonly rule: string;
|
|
@@ -80,7 +89,7 @@ interface DecisionAnswer {
|
|
|
80
89
|
}
|
|
81
90
|
type DecisionFailureReason = 'request-too-large' | 'aborted' | 'network' | 'http-status' | 'invalid-response';
|
|
82
91
|
interface DecisionDiagnostics {
|
|
83
|
-
readonly status: 'allow' | 'deny' | '
|
|
92
|
+
readonly status: 'allow' | 'deny' | 'failure' | 'timeout' | 'cancelled';
|
|
84
93
|
readonly stage: 'credential' | 'evidence' | 'request' | 'response';
|
|
85
94
|
readonly keyResolved: boolean;
|
|
86
95
|
readonly keySource: 'environment' | 'command' | 'none';
|
|
@@ -134,7 +143,7 @@ export declare function loadPolicy(paths?: PolicyPaths, framework?: 'classifier.
|
|
|
134
143
|
export declare function parseActionRequest(body: unknown): ActionRequest | null;
|
|
135
144
|
//#endregion
|
|
136
145
|
//#region src/request/render-verdict.d.ts
|
|
137
|
-
export declare function renderVerdict(verdict: Verdict): string
|
|
146
|
+
export declare function renderVerdict(verdict: Verdict): string;
|
|
138
147
|
//#endregion
|
|
139
148
|
//#region src/rules/classify-locally.d.ts
|
|
140
149
|
type LocalVerdict = {
|
|
@@ -145,4 +154,4 @@ type LocalVerdict = {
|
|
|
145
154
|
};
|
|
146
155
|
export declare function classifyLocally(payload: ActionRequest): LocalVerdict;
|
|
147
156
|
//#endregion
|
|
148
|
-
export type { ActionOutcome, ActionRequest, ClassifyOptions, Config, DecisionContext, LocalVerdict, ModelOutcome, PolicyPaths, ProviderConfig, Verdict };
|
|
157
|
+
export type { ActionOutcome, ActionRequest, ClassifyOptions, Config, DecisionContext, LocalVerdict, ModelOutcome, PolicyPaths, ProviderConfig, ScopeSource, Verdict };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as classifyWithModel, c as DEFAULT_CONFIG, d as
|
|
1
|
+
import { a as classifyWithModel, c as DEFAULT_CONFIG, d as resolveConfigPath, i as classifyLocally, l as loadConfig, n as parseActionRequest, o as parseModelVerdict, p as PRESETS, r as classifyAction, s as loadPolicy, t as renderVerdict, u as resolveApiKey } from "./render-verdict-CAg3AKWH.js";
|
|
2
2
|
export { DEFAULT_CONFIG, PRESETS, classifyAction, classifyLocally, classifyWithModel, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath };
|