auto-mode 0.8.0 → 0.9.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
@@ -38,7 +38,7 @@ Create the classifier configuration:
38
38
 
39
39
  Save it as `~/.config/auto-mode/config.json` and export your TypeSafe API key, or set the entry's
40
40
  `apiKeyCommand` to a command that prints it. [Configuration](./docs/guides/configuration.md) covers
41
- the registries, credentials, and migrating an older file.
41
+ the registries and credentials.
42
42
 
43
43
  ## Standing permissions
44
44
 
@@ -77,7 +77,6 @@ fit.
77
77
  | `auto-mode run --local-only` | Skip the model tier |
78
78
  | `auto-mode run --jev-only` | Require Jev with a 5-second API timeout |
79
79
  | `auto-mode print-prompt` | Print the selected provider's base policy |
80
- | `auto-mode config migrate` | Rewrite an older config file in place |
81
80
 
82
81
  The mod evaluates only ask decisions and preserves existing allow and deny decisions. It runs the
83
82
  CLI as a bounded child process with `--jev-only`. The `spark`, `claude`, `glm`, and `messages`
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { _ as string, f as resolveConfigPath, g as strictObject, h as number, l as DEFAULT_DENIAL_BUDGET, n as parseActionRequest, p as normalizeConfigFile, r as classifyAction, s as loadPolicy, t as renderVerdict, u as loadConfig } from "./render-verdict-DePMDXKz.js";
3
- import { appendFile, constants, copyFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { g as string, h as strictObject, l as DEFAULT_DENIAL_BUDGET, m as number, n as parseActionRequest, r as classifyAction, s as loadPolicy, t as renderVerdict, u as loadConfig } from "./render-verdict-CK9sRJpa.js";
3
+ import { appendFile, 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";
@@ -154,36 +154,6 @@ function buildJevOnlyConfig(config) {
154
154
  };
155
155
  }
156
156
  //#endregion
157
- //#region src/config/write-migrated-config.ts
158
- async function writeMigratedConfig(path) {
159
- let raw;
160
- try {
161
- raw = await readFile(path, "utf8");
162
- } catch (error) {
163
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return `${path} does not exist; nothing to migrate`;
164
- throw new Error(`${path} is unreadable`, { cause: error });
165
- }
166
- let json;
167
- try {
168
- json = JSON.parse(raw);
169
- } catch {
170
- throw new Error(`${path} is not valid JSON`);
171
- }
172
- const normalized = normalizeConfigFile(json, path);
173
- if (!normalized.isLegacy) return `${path} already uses the current shape`;
174
- const backup = `${path}.bak`;
175
- const staged = `${path}.migrating`;
176
- try {
177
- await copyFile(path, backup, constants.COPYFILE_EXCL);
178
- } catch (error) {
179
- 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 });
180
- throw error;
181
- }
182
- await writeFile(staged, `${JSON.stringify(normalized.file, null, 2)}\n`, { mode: 384 });
183
- await rename(staged, path);
184
- return `rewrote ${path} in the current shape; the old file is at ${backup}`;
185
- }
186
- //#endregion
187
157
  //#region src/diagnostics/write-action-diagnostic.ts
188
158
  async function writeActionDiagnostic(payload, entry) {
189
159
  const path = process.env["AUTO_MODE_DIAGNOSTICS_PATH"] ?? join(resolveStateDir(), "actions.jsonl");
@@ -221,7 +191,6 @@ const USAGE = `auto-mode — a permission classifier for the auto-mode Claude Co
221
191
  Usage:
222
192
  auto-mode run Read an action request on stdin, write a verdict on stdout
223
193
  auto-mode print-prompt Print the system prompt the classifier receives
224
- auto-mode config migrate Rewrite the config file from the old keys into the current shape
225
194
 
226
195
  Options:
227
196
  --classifier <path> Use this framework file instead of the shipped one
@@ -381,21 +350,9 @@ async function main(argv) {
381
350
  process.stdout.write(`${prompt}\n`);
382
351
  return 0;
383
352
  }
384
- if (command === "config" && args.positionals[1] === "migrate") return runConfigMigration();
385
353
  process.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
386
354
  return 2;
387
355
  }
388
- async function runConfigMigration() {
389
- const path = resolveConfigPath();
390
- try {
391
- const result = await writeMigratedConfig(path);
392
- process.stdout.write(`${result}\n`);
393
- return 0;
394
- } catch (error) {
395
- process.stderr.write(`auto-mode: ${error instanceof Error ? error.message : "config migration failed"}\n`);
396
- return 1;
397
- }
398
- }
399
356
  process.exitCode = await main(process.argv.slice(2));
400
357
  //#endregion
401
358
  export {};
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as classifyWithModel, c as DEFAULT_CONFIG, d as resolveApiKey, f as resolveConfigPath, i as classifyLocally, m as PRESETS, n as parseActionRequest, o as parseModelVerdict, r as classifyAction, s as loadPolicy, t as renderVerdict, u as loadConfig } from "./render-verdict-DePMDXKz.js";
1
+ import { a as classifyWithModel, c as DEFAULT_CONFIG, d as resolveApiKey, f as resolveConfigPath, i as classifyLocally, n as parseActionRequest, o as parseModelVerdict, p as PRESETS, r as classifyAction, s as loadPolicy, t as renderVerdict, u as loadConfig } from "./render-verdict-CK9sRJpa.js";
2
2
  export { DEFAULT_CONFIG, PRESETS, classifyAction, classifyLocally, classifyWithModel, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath };
@@ -5615,126 +5615,6 @@ function findPreset(id) {
5615
5615
  return Object.hasOwn(PRESETS, id) ? PRESETS[id] : void 0;
5616
5616
  }
5617
5617
  //#endregion
5618
- //#region src/config/normalize-config-file.ts
5619
- function normalizeConfigFile(json, path) {
5620
- if (!isRecord(json)) return {
5621
- file: json,
5622
- isLegacy: false,
5623
- warnings: []
5624
- };
5625
- const legacyKeys = Object.keys(json).filter((key) => LEGACY_KEYS.has(key));
5626
- if (legacyKeys.length === 0) return {
5627
- file: json,
5628
- isLegacy: false,
5629
- warnings: []
5630
- };
5631
- const currentKeys = Object.keys(json).filter((key) => !LEGACY_KEYS.has(key));
5632
- if (currentKeys.length > 0) throw new Error(`${path} mixes the old keys (${legacyKeys.join(", ")}) with the new ones (${currentKeys.join(", ")}); run \`auto-mode config migrate\` on a copy without the new keys`);
5633
- const parsed = legacyConfigSchema.safeParse(json);
5634
- if (!parsed.success) throw new Error(`${path} is not a valid config: ${prettifyError(parsed.error)}`);
5635
- const legacy = parsed.data;
5636
- const warnings = [`${path} uses the old config keys (${legacyKeys.join(", ")}); run \`auto-mode config migrate\` to rewrite it`, ...legacy.transcriptEntries === void 0 ? [] : [`${path} sets transcriptEntries, which has no effect; migration drops it`]];
5637
- return {
5638
- file: buildFileFromLegacy(legacy, path),
5639
- isLegacy: true,
5640
- warnings
5641
- };
5642
- }
5643
- function isRecord(value) {
5644
- return typeof value === "object" && value !== null && !Array.isArray(value);
5645
- }
5646
- const LEGACY_KEYS = /* @__PURE__ */ new Set([
5647
- "preset",
5648
- "provider",
5649
- "classifierPath",
5650
- "rulesPath",
5651
- "transcriptEntries",
5652
- "onFailure",
5653
- "claudeSettingsPath",
5654
- "minConfidence"
5655
- ]);
5656
- const text$1 = string().min(1);
5657
- const positive$1 = number().positive();
5658
- const legacyConfigSchema = strictObject({
5659
- preset: text$1.optional(),
5660
- provider: strictObject({
5661
- protocol: _enum(["messages", "system-one"]).optional(),
5662
- baseURL: text$1.optional(),
5663
- model: text$1.optional(),
5664
- apiKeyEnv: text$1.optional(),
5665
- apiKeyCommand: text$1.optional(),
5666
- reasoning: boolean().optional(),
5667
- maxTokens: positive$1.optional(),
5668
- timeoutMs: positive$1.optional()
5669
- }).optional(),
5670
- classifierPath: text$1.optional(),
5671
- rulesPath: text$1.optional(),
5672
- transcriptEntries: number().nonnegative().optional(),
5673
- onFailure: _enum(["defer", "deny"]).optional(),
5674
- claudeSettingsPath: text$1.nullable().optional(),
5675
- minConfidence: number().min(.5).max(1).optional()
5676
- });
5677
- function buildFileFromLegacy(legacy, path) {
5678
- const hasClassifier = legacy.preset !== void 0 || legacy.provider !== void 0;
5679
- const decision = {
5680
- ...hasClassifier ? { classifier: getLegacyClassifierID(legacy) } : {},
5681
- ...legacy.minConfidence === void 0 ? {} : { minConfidence: legacy.minConfidence },
5682
- ...legacy.onFailure === void 0 ? {} : { onFailure: legacy.onFailure }
5683
- };
5684
- const policy = {
5685
- ...legacy.rulesPath === void 0 ? {} : { rulesPath: legacy.rulesPath },
5686
- ...legacy.classifierPath === void 0 ? {} : { frameworkPath: legacy.classifierPath },
5687
- ...legacy.claudeSettingsPath === void 0 ? {} : { claudeSettingsPath: legacy.claudeSettingsPath }
5688
- };
5689
- return {
5690
- ...hasClassifier ? { classifiers: { [getLegacyClassifierID(legacy)]: buildClassifierEntry(getLegacyClassifierID(legacy), buildLegacyProvider(legacy, path)) } } : {},
5691
- ...Object.keys(decision).length === 0 ? {} : { decision },
5692
- ...Object.keys(policy).length === 0 ? {} : { policy }
5693
- };
5694
- }
5695
- function getLegacyClassifierID(legacy) {
5696
- if (legacy.preset !== void 0) return legacy.preset;
5697
- return legacy.provider !== void 0 && legacy.provider.protocol !== "system-one" ? "spark" : "jev";
5698
- }
5699
- function buildLegacyProvider(legacy, path) {
5700
- const preset = findPreset(getLegacyClassifierID(legacy));
5701
- if (preset === void 0) throw new Error(`${path} names an unknown preset '${legacy.preset}'; known presets are ${Object.keys(PRESETS).join(", ")}`);
5702
- const override = legacy.provider ?? {};
5703
- return {
5704
- protocol: override.protocol ?? preset.protocol ?? "messages",
5705
- baseURL: override.baseURL ?? preset.baseURL,
5706
- model: override.model ?? preset.model,
5707
- apiKeyEnv: override.apiKeyEnv ?? preset.apiKeyEnv,
5708
- apiKeyCommand: override.apiKeyCommand ?? preset.apiKeyCommand,
5709
- reasoning: override.reasoning ?? preset.reasoning,
5710
- maxTokens: override.maxTokens ?? preset.maxTokens,
5711
- timeoutMs: override.timeoutMs ?? preset.timeoutMs
5712
- };
5713
- }
5714
- const PROVIDER_FIELDS = [
5715
- "baseURL",
5716
- "model",
5717
- "apiKeyEnv",
5718
- "apiKeyCommand",
5719
- "reasoning",
5720
- "maxTokens",
5721
- "timeoutMs"
5722
- ];
5723
- function buildClassifierEntry(id, provider) {
5724
- const kind = pickEntryKind(id, provider);
5725
- const base = kind === "messages" ? MESSAGES_DEFAULTS : findPreset(kind) ?? {};
5726
- const fields = Object.fromEntries(PROVIDER_FIELDS.filter((field) => provider[field] !== void 0 && provider[field] !== base[field]).map((field) => [field, provider[field]]));
5727
- return {
5728
- ...kind === id ? {} : { kind },
5729
- ...fields
5730
- };
5731
- }
5732
- function pickEntryKind(id, provider) {
5733
- const preset = findPreset(id);
5734
- if (preset !== void 0 && (preset.protocol ?? "messages") === provider.protocol) return id;
5735
- return provider.protocol === "system-one" ? "jev" : "messages";
5736
- }
5737
- //#endregion
5738
5618
  //#region src/config/read-api-key-from-command.ts
5739
5619
  function readApiKeyFromCommand(command, options = {}) {
5740
5620
  const remainingMs = options.deadlineAt === void 0 ? 5e3 : options.deadlineAt - Date.now();
@@ -5826,8 +5706,7 @@ async function loadConfig(path = resolveConfigPath()) {
5826
5706
  } catch {
5827
5707
  throw new Error(`${path} is not valid JSON`);
5828
5708
  }
5829
- const normalized = normalizeConfigFile(json, path);
5830
- return buildConfig(normalized.file, path, normalized.warnings);
5709
+ return buildConfig(json, path);
5831
5710
  }
5832
5711
  const text = string().min(1);
5833
5712
  const positive = number().positive();
@@ -5851,11 +5730,11 @@ const configFileSchema = strictObject({
5851
5730
  claudeSettingsPath: text.nullable().optional()
5852
5731
  }).optional()
5853
5732
  });
5854
- function buildConfig(json, path, legacyWarnings) {
5733
+ function buildConfig(json, path) {
5855
5734
  const parsed = configFileSchema.safeParse(json);
5856
5735
  if (!parsed.success) throw new Error(`${path} is not a valid config: ${prettifyError(parsed.error)}`);
5857
5736
  const file = parsed.data;
5858
- const warnings = [...legacyWarnings];
5737
+ const warnings = [];
5859
5738
  const classifiers = buildRegistry(file.classifiers ?? {}, (id, entry) => parseClassifierEntry(id, entry));
5860
5739
  const scopeSources = buildRegistry(file.scopeSources ?? {}, (id, entry) => parseScopeSourceEntry(id, entry));
5861
5740
  warnings.push(...classifiers.dropped.map((line) => `${path}: classifiers.${line}`), ...scopeSources.dropped.map((line) => `${path}: scopeSources.${line}`));
@@ -7318,4 +7197,4 @@ function renderVerdict(verdict) {
7318
7197
  })).exhaustive();
7319
7198
  }
7320
7199
  //#endregion
7321
- export { string as _, classifyWithModel as a, DEFAULT_CONFIG as c, resolveApiKey as d, resolveConfigPath as f, strictObject as g, number as h, classifyLocally as i, DEFAULT_DENIAL_BUDGET as l, PRESETS as m, parseActionRequest as n, parseModelVerdict as o, normalizeConfigFile as p, classifyAction as r, loadPolicy as s, renderVerdict as t, loadConfig as u };
7200
+ export { classifyWithModel as a, DEFAULT_CONFIG as c, resolveApiKey as d, resolveConfigPath as f, string as g, strictObject as h, classifyLocally as i, DEFAULT_DENIAL_BUDGET as l, number as m, parseActionRequest as n, parseModelVerdict as o, PRESETS as p, classifyAction as r, loadPolicy as s, renderVerdict as t, loadConfig as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-mode",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "A permission classifier library and Claude Code mod, so agents that are not Claude can run unattended",
5
5
  "keywords": [
6
6
  "agent",