import { describe, expect, it } from "vitest"; import { APPROVAL_MODES, DEFAULT_APPROVAL_MODE, approvalModeFor, setDefaultApprovalMode, supportsApprovalMode, modelSwitchNeedsAsk, hasNativeAutoReview, isEmergencyApprovalDowngrade, isApprovalMode, levelWithin, } from "../shared/approval-mode.ts"; import { BUILT_IN_DRIVERS } from "./drivers/builtIn.ts"; import { createApprovalModeSupport } from "./harness-capabilities.ts"; describe("approval modes", () => { it("carries Full silently to any engine that runs it; Custom never crosses engines", () => { for (const driver of ["codex", "claudeAgent", "grokAgent", "antigravityAgent", "piAgent", "customAcp"]) { expect(modelSwitchNeedsAsk("ask", "codex", driver)).toBe(false); expect(modelSwitchNeedsAsk("auto", "codex", driver)).toBe(false); expect(modelSwitchNeedsAsk("full", "codex", driver)).toBe(false); expect(modelSwitchNeedsAsk("custom", "codex", driver)).toBe(driver !== "codex"); } expect(modelSwitchNeedsAsk("full", "codex", "claudeAgent")).toBe(false); expect(modelSwitchNeedsAsk("full", "claudeAgent", "openai-compat")).toBe(false); expect(modelSwitchNeedsAsk("edits", "claudeAgent", "codex")).toBe(true); expect(modelSwitchNeedsAsk("edits", "claudeAgent", "grokAgent")).toBe(false); expect(modelSwitchNeedsAsk("full", "claudeAgent", "claudeAgent")).toBe(false); expect(modelSwitchNeedsAsk("full", "codex", undefined)).toBe(false); }); it("only exposes implemented provider capabilities", () => { for (const driver of ["codex", "claudeAgent", "antigravityAgent", "cursorAgent", "grokAgent", "opencodeGo", "qwenAgent", "geminiAgent"]) { expect(supportsApprovalMode(driver, "full")).toBe(true); expect(supportsApprovalMode(driver, "custom")).toBe(driver === "codex"); // Qwen's `--approval-mode auto` is an LLM classifier; Gemini has no reviewer expect(hasNativeAutoReview(driver)).toBe(["codex", "claudeAgent", "cursorAgent", "grokAgent", "qwenAgent"].includes(driver)); expect(supportsApprovalMode(driver, "edits")).toBe(["claudeAgent", "grokAgent", "antigravityAgent", "qwenAgent", "geminiAgent"].includes(driver)); } expect(supportsApprovalMode(undefined, "full")).toBe(true); expect(supportsApprovalMode(undefined, "edits")).toBe(false); }); it("runs Full access, the default level, on every engine", () => { for (const { driverKind } of BUILT_IN_DRIVERS) expect(supportsApprovalMode(driverKind, "full"), driverKind).toBe(true); }); // No permissive mode of their own: the harness answers the permission // requests they leave, so Full runs there too. it.each([ "kimiAgent", "droidAgent", "hermesAgent", "customAcp", "piAgent", "unknown", ])("runs Full on %s without claiming native Auto or Edits", (driver) => { expect(supportsApprovalMode(driver, "ask")).toBe(true); expect(supportsApprovalMode(driver, "auto")).toBe(true); expect(supportsApprovalMode(driver, "edits")).toBe(false); expect(supportsApprovalMode(driver, "full")).toBe(true); expect(supportsApprovalMode(driver, "custom")).toBe(false); expect(hasNativeAutoReview(driver)).toBe(false); }); // The chat-completions family has no provider reviewer, so Auto behaves like Ask, but Full is implemented in the harness // (createOpenAIChatRuntime answers its own gate), so it is offered. it.each(["grok", "openai-compat", "minimax", "groq", "meta"])("offers harness-implemented Full on %s, but never native Auto", (driver) => { expect(supportsApprovalMode(driver, "ask")).toBe(true); expect(supportsApprovalMode(driver, "auto")).toBe(true); expect(supportsApprovalMode(driver, "full")).toBe(true); expect(supportsApprovalMode(driver, "edits")).toBe(false); expect(supportsApprovalMode(driver, "custom")).toBe(false); expect(hasNativeAutoReview(driver)).toBe(false); }); it("recognizes only the five durable values", () => { expect(APPROVAL_MODES).toEqual(["ask", "edits", "auto", "full", "custom"]); for (const mode of APPROVAL_MODES) expect(isApprovalMode(mode)).toBe(true); for (const value of [undefined, null, true, "automatic", "bypass"]) { expect(isApprovalMode(value)).toBe(false); } }); it("migrates the legacy Auto bit to safe Auto, never Full access", () => { expect(approvalModeFor({ autoApprove: true })).toBe("auto"); }); it("starts a bot or conversation nobody has set a level on at Full access", () => { expect(DEFAULT_APPROVAL_MODE).toBe("full"); expect(approvalModeFor({})).toBe("full"); // Every bot made over HTTP stores the legacy bit off beside no level. expect(approvalModeFor({ autoApprove: false })).toBe("full"); expect(approvalModeFor({ approvalMode: "ask", autoApprove: false })).toBe("ask"); }); it("keeps Ask as the default where the server says so (a shared company workspace)", () => { setDefaultApprovalMode("ask"); try { expect(approvalModeFor({})).toBe("ask"); expect(approvalModeFor({ autoApprove: false })).toBe("ask"); expect(approvalModeFor({ approvalMode: "full" })).toBe("full"); } finally { setDefaultApprovalMode(DEFAULT_APPROVAL_MODE); } expect(approvalModeFor({})).toBe("full"); }); it("prefers a valid explicit mode and fails closed around corrupt values", () => { expect(approvalModeFor({ approvalMode: "full", autoApprove: false })).toBe("full"); expect(approvalModeFor({ approvalMode: "custom", autoApprove: true })).toBe("custom"); expect(approvalModeFor({ approvalMode: "unknown", autoApprove: true })).toBe("auto"); expect(approvalModeFor({ approvalMode: "unknown", autoApprove: false })).toBe("ask"); }); it("keeps an unconfirmed elevated grant in Ask", () => { expect(approvalModeFor({ approvalMode: "full", approvalGrant: { requestId: "pending", mode: "full", phase: "prepared" }, })).toBe("ask"); expect(approvalModeFor({ approvalMode: "custom", approvalGrant: { requestId: "pending", mode: "custom", phase: "confirmed" }, })).toBe("ask"); }); it("permits only fail-closed recovery from an elevated mode", () => { expect(isEmergencyApprovalDowngrade("full", "ask")).toBe(true); expect(isEmergencyApprovalDowngrade("custom", "ask")).toBe(true); expect(isEmergencyApprovalDowngrade("full", "auto")).toBe(false); expect(isEmergencyApprovalDowngrade("ask", "auto")).toBe(false); expect(isEmergencyApprovalDowngrade("auto", "ask")).toBe(false); }); it("holds only the explicitly targeted thread during a composer grant", () => { const approvalGrant = { requestId: "pending", mode: "full", phase: "prepared", threadOnly: true, threadId: "target" }; expect(approvalModeFor({ approvalMode: "full", approvalGrant, threadId: "target" })).toBe("ask"); expect(approvalModeFor({ approvalMode: "full", approvalGrant, threadId: "other" })).toBe("full"); expect(approvalModeFor({ approvalMode: "full", approvalGrant })).toBe("ask"); }); }); describe("approval support bound to a registry", () => { const registry = { cliTarget: (instanceId: string) => instanceId === "codex-fixture" ? { driverKind: "codex" } : instanceId === "pi-fixture" ? { driverKind: "piAgent" } : null, }; const supports = createApprovalModeSupport(registry); it("resolves model selections through the registry", () => { const codex = { instanceId: "codex-fixture", model: "fixture" }; const pi = { instanceId: "pi-fixture", model: "fixture" }; expect(supports(codex, "full")).toBe(true); expect(supports(codex, "custom")).toBe(true); expect(supports(pi, "ask")).toBe(true); expect(supports(pi, "auto")).toBe(true); expect(supports(pi, "full")).toBe(true); expect(supports(pi, "custom")).toBe(false); }); it("answers an unknown instance like an unknown driver", () => { const ghost = { instanceId: "ghost", model: "fixture" }; expect(supports(ghost, "ask")).toBe(true); expect(supports(ghost, "edits")).toBe(false); expect(supports(ghost, "full")).toBe(true); expect(supports(undefined, "ask")).toBe(true); expect(supports(undefined, "full")).toBe(true); }); it("passes already-resolved driver views to the table unchanged", () => { expect(supports({ driverKind: "codex" }, "custom")).toBe(true); expect(supports({ driverKind: "piAgent" }, "full")).toBe(true); expect(supports({ driverKind: undefined }, "ask")).toBe(true); expect(supports({ driverKind: undefined }, "full")).toBe(true); }); it("agrees with the pure table across every mode", () => { for (const driverKind of ["codex", "claudeAgent", "piAgent", undefined] as const) { for (const mode of APPROVAL_MODES) { expect(supports({ driverKind }, mode)).toBe(supportsApprovalMode(driverKind, mode)); } } }); }); describe("a level within another", () => { it("a level is within itself and any looser one; custom compares only to itself", () => { expect(levelWithin("ask", "full")).toBe(true); expect(levelWithin("full", "full")).toBe(true); expect(levelWithin("full", "ask")).toBe(false); expect(levelWithin("auto", "edits")).toBe(false); expect(levelWithin("custom", "custom")).toBe(true); expect(levelWithin("custom", "full")).toBe(false); expect(levelWithin("ask", "custom")).toBe(false); }); });