diff --git a/assistant/src/cli/commands/autonomy.ts b/assistant/src/cli/commands/autonomy.ts deleted file mode 100644 index 54ba2e862bd..00000000000 --- a/assistant/src/cli/commands/autonomy.ts +++ /dev/null @@ -1,371 +0,0 @@ -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; -import { dirname, join } from "node:path"; - -import type { Command } from "commander"; - -import { getWorkspaceDir } from "../../util/platform.js"; -import { registerCommand } from "../lib/register-command.js"; -import { log } from "../logger.js"; - -// --------------------------------------------------------------------------- -// Types & constants -// --------------------------------------------------------------------------- - -type AutonomyTier = "auto" | "draft" | "notify"; - -const AUTONOMY_TIERS: readonly AutonomyTier[] = ["auto", "draft", "notify"]; - -interface AutonomyConfig { - defaultTier: AutonomyTier; - channelDefaults: Record; - categoryOverrides: Record; - contactOverrides: Record; -} - -const DEFAULT_AUTONOMY_CONFIG: AutonomyConfig = { - defaultTier: "notify", - channelDefaults: {}, - categoryOverrides: {}, - contactOverrides: {}, -}; - -// --------------------------------------------------------------------------- -// Config persistence -// --------------------------------------------------------------------------- - -function getConfigPath(): string { - return join(getWorkspaceDir(), "autonomy.json"); -} - -function isValidTier(value: unknown): value is AutonomyTier { - return ( - typeof value === "string" && AUTONOMY_TIERS.includes(value as AutonomyTier) - ); -} - -function validateTierRecord(raw: unknown): Record { - if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {}; - const result: Record = {}; - for (const [key, value] of Object.entries(raw as Record)) { - if (isValidTier(value)) { - result[key] = value; - } - } - return result; -} - -function validateConfig(raw: unknown): AutonomyConfig { - if (!raw || typeof raw !== "object" || Array.isArray(raw)) { - return structuredClone(DEFAULT_AUTONOMY_CONFIG); - } - const obj = raw as Record; - return { - defaultTier: isValidTier(obj.defaultTier) - ? obj.defaultTier - : DEFAULT_AUTONOMY_CONFIG.defaultTier, - channelDefaults: validateTierRecord(obj.channelDefaults), - categoryOverrides: validateTierRecord(obj.categoryOverrides), - contactOverrides: validateTierRecord(obj.contactOverrides), - }; -} - -function loadConfig(): AutonomyConfig { - const configPath = getConfigPath(); - if (!existsSync(configPath)) { - return structuredClone(DEFAULT_AUTONOMY_CONFIG); - } - try { - const raw = readFileSync(configPath, "utf-8"); - return validateConfig(JSON.parse(raw)); - } catch { - log.error("Warning: failed to parse autonomy config; using defaults"); - return structuredClone(DEFAULT_AUTONOMY_CONFIG); - } -} - -function saveConfig(config: AutonomyConfig): void { - const configPath = getConfigPath(); - mkdirSync(dirname(configPath), { recursive: true }); - writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n"); -} - -function applyUpdate(updates: Partial): AutonomyConfig { - const current = loadConfig(); - if (updates.defaultTier !== undefined) { - current.defaultTier = updates.defaultTier; - } - if (updates.channelDefaults !== undefined) { - current.channelDefaults = { - ...current.channelDefaults, - ...updates.channelDefaults, - }; - } - if (updates.categoryOverrides !== undefined) { - current.categoryOverrides = { - ...current.categoryOverrides, - ...updates.categoryOverrides, - }; - } - if (updates.contactOverrides !== undefined) { - current.contactOverrides = { - ...current.contactOverrides, - ...updates.contactOverrides, - }; - } - saveConfig(current); - return current; -} - -// --------------------------------------------------------------------------- -// Output helpers -// --------------------------------------------------------------------------- - -function outputJson(data: unknown): void { - process.stdout.write(JSON.stringify(data) + "\n"); -} - -function getJson(cmd: Command): boolean { - let c: Command | null = cmd; - while (c) { - if ((c.opts() as { json?: boolean }).json) return true; - c = c.parent; - } - return false; -} - -function formatConfigForHuman(config: AutonomyConfig): string { - const lines: string[] = [` Default tier: ${config.defaultTier}`]; - - const channelEntries = Object.entries(config.channelDefaults); - if (channelEntries.length > 0) { - lines.push(" Channel defaults:"); - for (const [channel, tier] of channelEntries) { - lines.push(` ${channel}: ${tier}`); - } - } else { - lines.push(" Channel defaults: (none)"); - } - - const categoryEntries = Object.entries(config.categoryOverrides); - if (categoryEntries.length > 0) { - lines.push(" Category overrides:"); - for (const [category, tier] of categoryEntries) { - lines.push(` ${category}: ${tier}`); - } - } else { - lines.push(" Category overrides: (none)"); - } - - const contactEntries = Object.entries(config.contactOverrides); - if (contactEntries.length > 0) { - lines.push(" Contact overrides:"); - for (const [contactId, tier] of contactEntries) { - lines.push(` ${contactId}: ${tier}`); - } - } else { - lines.push(" Contact overrides: (none)"); - } - - return lines.join("\n"); -} - -// --------------------------------------------------------------------------- -// Command registration -// --------------------------------------------------------------------------- - -export function registerAutonomyCommand(program: Command): void { - // JARVIS-745: autonomy.json is file-write-only; no daemon consumer. Tagged local until resolved. - registerCommand(program, { - name: "autonomy", - transport: "local", - description: "View and configure autonomy tiers", - build: (autonomy) => { - autonomy.option("--json", "Machine-readable JSON output"); - - autonomy.addHelpText( - "after", - ` -Autonomy tiers control how independently the assistant acts on each message: - - auto Assistant acts independently — sends messages, executes actions - without asking for permission. - draft Assistant creates drafts for your approval before sending or - executing. You review and confirm each action. - notify Assistant notifies you about incoming messages and events but - does not act or draft. Purely informational. - -Resolution order (first match wins): - 1. Contact override — per-contact tier set via --contact - 2. Category override — per-category tier set via --category - 3. Channel default — per-channel tier set via --channel - 4. Global default — the fallback tier set via --default - -Config is stored in /autonomy.json (resolved via getWorkspaceDir()). - -Examples: - $ assistant autonomy get - $ assistant autonomy set --default draft - $ assistant autonomy set --channel telegram --tier auto`, - ); - - autonomy - .command("get") - .description("Show current autonomy configuration") - .addHelpText( - "after", - ` -Prints the full autonomy configuration: the global default tier, per-channel -defaults, category overrides, and contact overrides. Sections with no entries -are shown as "(none)". - -Pass --json (on the parent command) for machine-readable output containing -the complete config object. - -Examples: - $ assistant autonomy get - $ assistant autonomy --json get`, - ) - .action((_opts: Record, cmd: Command) => { - const json = getJson(cmd); - const config = loadConfig(); - if (json) { - outputJson({ ok: true, config }); - } else { - process.stdout.write("Autonomy configuration:\n\n"); - process.stdout.write(formatConfigForHuman(config) + "\n"); - } - }); - - autonomy - .command("set") - .description("Set autonomy tier for default, channel, category, or contact") - .option("--default ", "Set the global default tier") - .option("--channel ", "Channel to configure") - .option("--category ", "Category to configure") - .option("--contact ", "Contact to configure") - .option("--tier ", "Tier to set (auto, draft, notify)") - .addHelpText( - "after", - ` -Four targeting modes — provide one of the following per invocation. If multiple -are given, the first match is applied in this priority order: - - --default Set the global default tier. The - value is the argument itself — do not - combine with --tier. - --channel --tier Set the default tier for a specific channel. - --category --tier Set the tier override for a message category. - --contact --tier Set the tier override for a specific contact. - -Valid tier values: auto, draft, notify. - -Each call merges into the existing config — it does not replace other entries. -For example, setting a channel default leaves all other channel defaults, -category overrides, and contact overrides intact. - -Examples: - $ assistant autonomy set --default draft - $ assistant autonomy set --channel telegram --tier auto - $ assistant autonomy set --category billing --tier notify - $ assistant autonomy set --contact c_8f3a1b2d --tier draft`, - ) - .action( - ( - opts: { - default?: string; - channel?: string; - category?: string; - contact?: string; - tier?: string; - }, - cmd: Command, - ) => { - const json = getJson(cmd); - - if (opts.default) { - if (!isValidTier(opts.default)) { - outputJson({ - ok: false, - error: `Invalid tier "${opts.default}". Must be one of: ${AUTONOMY_TIERS.join(", ")}`, - }); - process.exitCode = 1; - return; - } - const config = applyUpdate({ defaultTier: opts.default }); - if (json) { - outputJson({ ok: true, config }); - } else { - log.info(`Set global default tier to "${opts.default}".`); - } - return; - } - - if (!opts.tier) { - outputJson({ - ok: false, - error: "Missing --tier. Use --tier .", - }); - process.exitCode = 1; - return; - } - if (!isValidTier(opts.tier)) { - outputJson({ - ok: false, - error: `Invalid tier "${opts.tier}". Must be one of: ${AUTONOMY_TIERS.join(", ")}`, - }); - process.exitCode = 1; - return; - } - - if (opts.channel) { - const config = applyUpdate({ - channelDefaults: { [opts.channel]: opts.tier }, - }); - if (json) { - outputJson({ ok: true, config }); - } else { - log.info( - `Set channel "${opts.channel}" default to "${opts.tier}".`, - ); - } - return; - } - - if (opts.category) { - const config = applyUpdate({ - categoryOverrides: { [opts.category]: opts.tier }, - }); - if (json) { - outputJson({ ok: true, config }); - } else { - log.info( - `Set category "${opts.category}" override to "${opts.tier}".`, - ); - } - return; - } - - if (opts.contact) { - const config = applyUpdate({ - contactOverrides: { [opts.contact]: opts.tier }, - }); - if (json) { - outputJson({ ok: true, config }); - } else { - log.info( - `Set contact "${opts.contact}" override to "${opts.tier}".`, - ); - } - return; - } - - log.error( - "Specify one of: --default , --channel --tier , " + - "--category --tier , or --contact --tier .", - ); - process.exitCode = 1; - }, - ); - }, - }); -} diff --git a/assistant/src/cli/commands/completions.ts b/assistant/src/cli/commands/completions.ts index 39ea3dcd0e7..32a71487940 100644 --- a/assistant/src/cli/commands/completions.ts +++ b/assistant/src/cli/commands/completions.ts @@ -43,7 +43,6 @@ Examples: trust: ["list"], memory: ["status", "backfill", "cleanup", "query", "rebuild-index"], contacts: ["list", "invites", "get", "merge"], - autonomy: ["get", "set"], }; const topLevel = [ "conversations", @@ -52,7 +51,6 @@ Examples: "trust", "memory", "contacts", - "autonomy", "audit", "completions", "help", @@ -132,7 +130,6 @@ _assistant() { 'trust:View trust rules' 'memory:Manage long-term memory' 'contacts:Manage the contact graph' - 'autonomy:View and configure autonomy tiers' 'audit:Show recent tool invocations' 'completions:Generate shell completion script' 'help:Display help' @@ -171,7 +168,6 @@ function generateFishCompletion( trust: "View trust rules", memory: "Manage long-term memory", contacts: "Manage the contact graph", - autonomy: "View and configure autonomy tiers", audit: "Show recent tool invocations", completions: "Generate shell completion script", help: "Display help", diff --git a/assistant/src/cli/program.ts b/assistant/src/cli/program.ts index 1921801d2d8..2e87eaa2ce7 100644 --- a/assistant/src/cli/program.ts +++ b/assistant/src/cli/program.ts @@ -10,7 +10,6 @@ import { APP_VERSION } from "../version.js"; import { registerAttachmentCommand } from "./commands/attachment.js"; import { registerAuditCommand } from "./commands/audit.js"; import { registerAuthCommand } from "./commands/auth.js"; -import { registerAutonomyCommand } from "./commands/autonomy.js"; import { registerAvatarCommand } from "./commands/avatar.js"; import { registerBackupCommand } from "./commands/backup.js"; import { registerBashCommand } from "./commands/bash.js"; @@ -79,7 +78,6 @@ Examples: registerAttachmentCommand(program); registerAuditCommand(program); registerAuthCommand(program); - registerAutonomyCommand(program); registerAvatarCommand(program); registerBackupCommand(program); registerBashCommand(program); diff --git a/gateway/src/risk/command-registry.test.ts b/gateway/src/risk/command-registry.test.ts index 02e928e7264..46593efdd78 100644 --- a/gateway/src/risk/command-registry.test.ts +++ b/gateway/src/risk/command-registry.test.ts @@ -540,7 +540,6 @@ describe("command-registry", () => { "attachment", "audit", "auth", - "autonomy", "avatar", "backup", "bash", diff --git a/gateway/src/risk/command-registry/commands/assistant.ts b/gateway/src/risk/command-registry/commands/assistant.ts index ec839d72690..5559e66f3ea 100644 --- a/gateway/src/risk/command-registry/commands/assistant.ts +++ b/gateway/src/risk/command-registry/commands/assistant.ts @@ -18,9 +18,6 @@ const ASSISTANT_SUPPORTED_COMMAND_PATHS = [ "audit", "auth", "auth info", - "autonomy", - "autonomy get", - "autonomy set", "avatar", "avatar generate", "avatar set", @@ -364,7 +361,6 @@ const riskOverrides: AssistantRiskOverride[] = [ // Mutating assistant state / external side effects { path: "attachment register", risk: "medium" }, - { path: "autonomy set", risk: "medium" }, { path: "avatar generate", risk: "low" }, { path: "avatar set", risk: "low" }, { path: "avatar remove", risk: "low" }, diff --git a/skills/catalog.json b/skills/catalog.json index d92b65aad9e..33f50692709 100644 --- a/skills/catalog.json +++ b/skills/catalog.json @@ -842,7 +842,7 @@ } }, "compatibility": "Designed for Vellum personal assistants", - "updatedAt": "2026-04-29T08:26:02-04:00" + "updatedAt": "2026-05-11T04:56:56Z" }, { "id": "vellum-skills-catalog", diff --git a/skills/vellum-self-knowledge/SKILL.md b/skills/vellum-self-knowledge/SKILL.md index 228fb938f6b..31b197f39c5 100644 --- a/skills/vellum-self-knowledge/SKILL.md +++ b/skills/vellum-self-knowledge/SKILL.md @@ -40,7 +40,6 @@ The CLI is the single source of truth for anything about the running assistant's | Stored credentials | `assistant credentials list` | | API keys | `assistant keys list` | | MCP servers | `assistant mcp list` | -| Autonomy tiers | `assistant autonomy get` | | Watchers | `assistant watchers list` | | Token usage/costs | `assistant usage totals` / `assistant usage breakdown --group-by provider` | | Version | `assistant --version` |