Skip to content

Permission modes

ActiveCli.Session.PermissionMode — how much a session may do without asking first. A closed set of named modes rather than a string, because each one has to survive two translations — into the flag a CLI is spawned with, and back out of what the CLI reports about itself — and a bare string would let them drift apart silently.

class PermissionMode {
static readonly PLAN: PermissionMode;
static readonly ASK_BEFORE_EDIT: PermissionMode;
static readonly AUTO_EDIT: PermissionMode;
static readonly AUTO: PermissionMode;
static readonly BYPASS: PermissionMode;
readonly name: string;
static named(name: string | undefined | null): PermissionMode | null;
toString(): string;
}
Mode name Meaning
PLAN plan Plan only: propose, never edit.
ASK_BEFORE_EDIT ask_before_edit Ask before every edit.
AUTO_EDIT auto_edit Edits go through; other tools still ask.
AUTO auto Decide autonomously.
BYPASS bypass Ask for nothing at all.

The constructor is private, so no sixth mode can appear that the adapters do not know how to spell. named() answers null for anything outside the set.

PermissionMode.PLAN // propose, never edit
PermissionMode.ASK_BEFORE_EDIT // ask before every edit
PermissionMode.AUTO_EDIT // edits go through; other tools still ask
PermissionMode.AUTO // decide autonomously
PermissionMode.BYPASS // ask for nothing
PermissionMode.named('plan'); // => PermissionMode.PLAN
PermissionMode.named('nonsense'); // => null

ActiveCli.Adapter.Claude.ClaudePermissionFlag — translates between PermissionMode and the flag names the Claude CLI uses for the same idea.

class ClaudePermissionFlag {
forMode(mode: PermissionMode): string;
toMode(flag: string | undefined | null): PermissionMode | null;
}
PermissionMode CLI flag
PLAN plan
ASK_BEFORE_EDIT default
AUTO_EDIT acceptEdits
AUTO auto
BYPASS bypassPermissions

Both directions are needed and must agree: we spawn with the flag, and the CLI then announces its mode using that same vocabulary — on system/init at spawn, and again on system/status when it changes the mode itself. The reverse map is derived from the forward one, which is what keeps the two from drifting.

Note that default names the ask-before-edits mode — it does not mean “follow the user’s settings”, which is why ClaudeArgv omits the flag entirely when no mode is established. Passing --permission-mode default would override the user’s configured setting rather than defer to it.

ActiveCli.Session.ModeChange — decides whether a running CLI can serve a request, or has to be restarted.

class ModeChange {
constructor(liveMode: PermissionMode | null);
requiresRestart(requested: PermissionMode | null | undefined): boolean;
}

--permission-mode is a spawn-time flag, and no documented command changes it in place. Honouring a mode change mid-conversation therefore means killing the CLI and spawning it again.

Two cases are easy to get wrong:

Case Answer Why
No mode requested false Not a mode to compare against. Restarting would tear down a working process to spawn an identical one.
Live mode unknown (null) true Happens for a session adopted after a host restart. Reusing gambles on a mode nobody observed, and the losing side is edits under looser permissions than the user chose.

Reach it through Session.requiresRestartFor() rather than constructing one by hand — the session already tracks the live mode.

if (session.requiresRestartFor(PermissionMode.PLAN)) {
session.stop();
session = Session.start(
new ClaudeAdapter(),
new SpawnOptions({ workingDirectory, sessionId, resume: true, permissionMode: PermissionMode.PLAN }),
);
}

Through Command none of this needs handling by hand: setPermissions() records the intent and the next send() respawns if it has to.