# Understanding the Three Permission Modes in Craft Agents

> Master Craft Agents permission modes: Explore, Ask to Edit, and Execute. Learn how to easily switch between them programmatically or interactively using simple commands and shortcuts.

- Repository: [Craft Ai Agents/craft-agents-oss](https://github.com/craft-ai-agents/craft-agents-oss)
- Tags: deep-dive
- Published: 2026-07-04

---

**Craft Agents enforces three permission modes—Explore (safe), Ask to Edit (ask), and Execute (allow-all)—that govern tool access, and you can switch between them programmatically using `setPermissionMode()` or interactively via `cyclePermissionMode()` and the Shift+Tab shortcut.**

The craft-ai-agents/craft-agents-oss repository implements this permission system in the shared `@craft-agent` package. These **permission modes** are deliberately isolated per-session, ensuring that mode switches never leak state between concurrent agent sessions.

## The Three Permission Modes

The mapping between internal keys and UI-facing names is defined in [`packages/shared/src/agent/mode-types.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/packages/shared/src/agent/mode-types.ts) at lines 24-30. Each mode controls what tools and commands an agent may execute.

### Explore Mode (safe)

**Explore Mode** is the read-only default. In `safe` mode, all write-type tools—such as Write and Edit—are blocked, and the agent operates without asking for confirmation. This mode is ideal for investigating codebases or generating explanations without risk of modification.

### Ask to Edit Mode (ask)

**Ask to Edit Mode** enables interactive operation. When set to `ask`, the agent prompts the user for confirmation before performing any potentially dangerous operation. This balances automation with oversight for sensitive tasks.

### Execute Mode (allow-all)

**Execute Mode** removes all restrictions. Under the `allow-all` internal key, no permission checks are performed, allowing the agent to write, edit, and execute commands freely. Use this mode only when full automation is required and the environment is secure.

## How to Switch Permission Modes

The public API for changing modes resides in [`packages/shared/src/agent/mode-manager.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/packages/shared/src/agent/mode-manager.ts). The system stores per-session state in a `Map` managed by the singleton `modeManager` instance (lines 30-33), preventing global mutable state contamination.

### Programmatic Mode Assignment

To set a specific mode directly, call `setPermissionMode()` exported from [`mode-manager.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/mode-manager.ts) (lines 82-99). This function accepts a `sessionId`, the target mode (`'safe'`, `'ask'`, or `'allow-all'`), and an options object:

```typescript
import { setPermissionMode } from '@craft-agent/shared/agent';

const sessionId = 'my-session';

// Switch to Ask to Edit mode
setPermissionMode(sessionId, 'ask', { changedBy: 'user' });

```

### Interactive Mode Cycling

For UI interactions, the `cyclePermissionMode` function traverses the ordered list `PERMISSION_MODE_ORDER`—defined as `['safe','ask','allow-all']`—and updates the mode for the current session (lines 34-42). The Electron UI binds **Shift+Tab** to this function, allowing users to toggle modes without code:

```typescript
import { cyclePermissionMode } from '@craft-agent/shared/agent';

function toggleMode(sessionId: string) {
  const newMode = cyclePermissionMode(sessionId);
  console.log(`Switched to ${newMode}`);
}

```

### React Integration

The manager emits callbacks via `onStateChange` and supports React subscribers through `useSyncExternalStore` (lines 101-108). The `ModeState` record tracks the current mode, previous mode, a monotonic version counter, and timestamps for debugging (lines 85-99):

```tsx
import { useSyncExternalStore } from 'react';
import { subscribeModeChanges, getModeState } from '@craft-agent/shared/agent';

function PermissionModeIndicator({ sessionId }: { sessionId: string }) {
  const state = useSyncExternalStore(
    cb => subscribeModeChanges(sessionId, cb),
    () => getModeState(sessionId)
  );

  return (
    <div>
      Mode: {state.permissionMode} (v{state.modeVersion})
    </div>
  );
}

```

## Practical Code Examples

### Switching from Explore to Execute via CLI

When automating agent workflows from a command-line script, import `setPermissionMode` and `getPermissionMode` to verify transitions:

```typescript
import { setPermissionMode, getPermissionMode } from '@craft-agent/shared/agent';

async function enableExecute(sessionId: string) {
  console.log('Current mode:', getPermissionMode(sessionId)); // safe
  setPermissionMode(sessionId, 'allow-all', { changedBy: 'user' });
  console.log('New mode:', getPermissionMode(sessionId));    // allow-all
}

```

### Reading Current Mode State

Before initiating tool calls, check the current permission level:

```typescript
import { getPermissionMode } from '@craft-agent/shared/agent';

const sessionId = 'my-session';
const current = getPermissionMode(sessionId);
// Returns: 'safe' | 'ask' | 'allow-all'

```

### RPC and Server Integration

The same API powers the Electron UI and server-side handlers. The RPC endpoint in [`apps/electron/src/shared/routes.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/apps/electron/src/shared/routes.ts) (lines 81-82) and the server implementation in [`packages/server-core/src/handlers/rpc/sessions.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/packages/server-core/src/handlers/rpc/sessions.ts) (lines 322-323) both delegate to `setPermissionMode` to honor remote requests.

## Summary

- **Three permission modes** exist: **Explore** (`safe`), **Ask to Edit** (`ask`), and **Execute** (`allow-all`), defined in [`mode-types.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/mode-types.ts).
- **State isolation** is enforced per-session via a singleton `modeManager` using a `Map` structure, preventing cross-session contamination.
- **Switching methods** include `setPermissionMode()` for direct assignment and `cyclePermissionMode()` for sequential cycling through the `PERMISSION_MODE_ORDER` array.
- **UI integration** supports Shift+Tab shortcuts and React subscriptions via `useSyncExternalStore` with immediate callback notifications on state changes.
- **RPC support** allows remote mode changes through the Electron and server-core handlers.

## Frequently Asked Questions

### What is the default permission mode when starting a new session?

The default mode is **Explore** (`safe`), which operates in read-only mode to prevent accidental modifications. You can verify this by calling `getPermissionMode(sessionId)` immediately after session initialization.

### Can permission modes be changed remotely via API calls?

Yes. The server implementation in [`packages/server-core/src/handlers/rpc/sessions.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/packages/server-core/src/handlers/rpc/sessions.ts) (lines 322-323) exposes RPC endpoints that delegate to `setPermissionMode`, allowing remote clients to change a session's permission mode securely.

### How does the UI know when a permission mode changes?

The `modeManager` emits callbacks through `onStateChange` and notifies React subscribers via `useSyncExternalStore` (lines 101-108 in [`mode-manager.ts`](https://github.com/craft-ai-agents/craft-agents-oss/blob/main/mode-manager.ts)). This ensures the UI updates instantly when modes transition, either programmatically or through the Shift+Tab shortcut.

### Is there any global state shared between different sessions?

No. The implementation deliberately isolates state per-session using a `Map` data structure in the `modeManager` singleton (lines 30-33). Each session maintains its own `ModeState` record, ensuring that changing modes in one session never affects another.