> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI Backend Protocol

> Plug an external CLI (Claude Code, future: Codex, Gemini) in as a first-class Agent backend

<Warning>
  **Deprecated — use [Runtime Selection](/docs/features/runtime-selection) instead.** `cli_backend` still works through 2.0.0 but emits `DeprecationWarning`. For YAML migration, run `praisonai doctor fix --execute` (or the equivalent `praisonai doctor runtime --fix --execute`) — see [Runtime Config Migration](/docs/features/doctor-runtime-migration).

  ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  # Before
  agent = Agent(instructions="...", cli_backend="claude-code")

  # After
  agent = Agent(instructions="...", runtime="claude-code")
  ```
</Warning>

<Note>
  **Stricter protocol check (PR #3252):** `cli_backend=` now validates the argument with `isinstance(obj, CliBackendProtocol)` (the `@runtime_checkable` protocol from `praisonaiagents.cli_backend.protocols`). Objects that merely expose `execute()` / `stream()` but lack `config` / `capabilities()` (e.g. a `BaseCLIIntegration` coding-CLI tool) now raise `TypeError` at construction with a hint to pass them via `tools=[...]` instead. Previously they were accepted silently and crashed deep in the agent loop.

  The rejection message (verbatim):

  ```
  <ClassName> exposes execute()/stream() but is not a CliBackendProtocol (it lacks
  config/capabilities() and returns a plain str). If this is a coding-CLI
  integration, pass it as a tool via tools=[...], not cli_backend=.
  ```
</Note>

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(name="cli-worker", instructions="Run this turn via an external CLI backend.", runtime="claude-code")
agent.start("Implement the login form.")
```

The user runs an agent turn; the CLI backend resolver spawns the external CLI and returns the result through the same Agent API.

CLI Backends let you run an agent's turn through an external CLI tool (like Claude Code) instead of a Python LLM client, while keeping the same `Agent`/`Task` API.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "CLI Backend Flow"
        A[📋 Agent Input] --> B[🧠 Resolver]
        B --> C[🔌 Backend]
        C --> D[💻 CLI Process]
        D --> E[✅ Result]
    end
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef resolver fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef backend fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    
    classDef agent fill:#8B0000,color:#fff
    classDef tool fill:#189AB4,color:#fff
    class A input
    class B resolver
    class C,D backend
    class E result
```

## Quick Start

<Steps>
  <Step title="Simplest: CLI flag">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai "Refactor utils.py" --cli-backend claude-code
    ```
  </Step>

  <Step title="Declarative: YAML">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    framework: praisonai
    topic: coding
    roles:
      coder:
        role: Code refactorer
        goal: Refactor Python modules
        backstory: Senior engineer
        cli_backend: claude-code   # string form
        tasks:
          refactor:
            description: Refactor utils.py
            expected_output: Refactored code
    ```
  </Step>

  <Step title="YAML with overrides">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    roles:
      coder:
        role: Code refactorer
        goal: Refactor Python modules
        backstory: Senior engineer
        cli_backend:
          id: claude-code
          overrides:
            timeout_ms: 60000
        tasks:
          refactor:
            description: Refactor utils.py
            expected_output: Refactored code
    ```
  </Step>

  <Step title="Discover what's registered">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai backends list
    # claude-code
    # codex-cli
    # grok-cli
    # gemini-cli
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Loader as YAML/Python loader
    participant Validator as Framework check
    participant Resolver as resolve_cli_backend_config
    participant Backend as ClaudeCodeBackend
    participant Agent

    User->>Loader: cli_backend = "claude-code" (or dict)
    Loader->>Validator: framework == "praisonai"?
    alt framework is praisonai
        Validator->>Resolver: pass-through
        Resolver->>Backend: instantiate (or use cached)
        Backend-->>Agent: ready to delegate turns
    else any other framework
        Validator-->>User: ValueError (fast fail)
    end
```

| Step | Component | Purpose                                           |
| ---- | --------- | ------------------------------------------------- |
| 1    | User/CLI  | Specifies backend via flag or YAML                |
| 2    | Resolver  | Factory pattern for backend creation              |
| 3    | Backend   | Protocol implementation (e.g., ClaudeCodeBackend) |
| 4    | Process   | External CLI subprocess execution                 |
| 5    | Result    | Parsed response returned to Agent                 |

### Permission modes (Claude Code backend)

| Setting                         | Default (PR #2122) | Notes                           |
| ------------------------------- | ------------------ | ------------------------------- |
| `--permission-mode`             | `default`          | Was `bypassPermissions`         |
| `ClaudeCodeBackend(unsafe=...)` | `False`            | Set `True` + env var for bypass |

To opt into bypass:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export PRAISONAI_CLAUDE_BYPASS_PERMISSIONS=1
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.cli_backends.claude import ClaudeCodeBackend

backend = ClaudeCodeBackend(config={...}, unsafe=True)
```

With only `unsafe=True` or only the env var set, the backend overrides to `default` mode and logs a warning.

***

## Configuration Surfaces

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{How do I want to configure a CLI backend?}
    Q -->|Quickest, ad-hoc command| A[--cli-backend on the CLI]
    Q -->|Versioned, per-role config| B[cli_backend in YAML - string form]
    Q -->|Need to tweak timeouts/args| C[cli_backend in YAML - dict with overrides]
    Q -->|Building a custom backend| D[register_cli_backend in Python]
    
    classDef question fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef option fill:#189AB4,stroke:#7C90A0,color:#fff
    
    class Q question
    class A,B,C,D option
```

***

## Observability

Confirm that PraisonAI is skipping the LiteLLM HTTP call and delegating to a CLI subprocess.

A user adopts `cli_backend="codex-cli"` and wants proof delegation is actually happening. They set `PRAISONAI_CLI_BACKEND_DEBUG=1`, enable `cli_backend_tracer`, and see one log line per turn — with the prompt already redacted so the log stream is safe to ship to an aggregator. If it silently isn't delegating, no log line appears — and they know something is wrong at config time.

### Env var

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export PRAISONAI_CLI_BACKEND_DEBUG=1
# or the standard knob
export LOGLEVEL=DEBUG
```

### Plugin

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
pip install praisonai-plugins
praisonai plugins enable cli_backend_tracer
```

### Hook (programmatic)

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.hooks import HookRegistry, HookEvent, HookResult

registry = HookRegistry()

@registry.on(HookEvent.CLI_BACKEND_EXECUTE)
def trace(event_data):
    print(f"{event_data.backend} → {event_data.command}")
    return HookResult.allow()

agent = Agent(name="assistant", cli_backend="codex-cli", hooks=registry)
agent.start("Refactor utils.py")
```

See [Hook Events → CLI Backend Events](/docs/docs/features/hook-events#cli-backend-events) for the full payload reference. The `command` field is redacted at the serialisation boundary — user prompts and system instructions never leak into log sinks.

***

## The `cli_backend` YAML Field

| Shape                  | Example                                                          | Behavior                                                                            |
| ---------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Omitted                | *(field absent)*                                                 | No backend used; Agent uses normal LLM client. No warning logged.                   |
| String                 | `cli_backend: claude-code`                                       | Resolves via `resolve_cli_backend("claude-code")`.                                  |
| Dict                   | `cli_backend: {id: claude-code, overrides: {timeout_ms: 60000}}` | Resolves via `resolve_cli_backend("claude-code", overrides={...})`.                 |
| Empty string           | `cli_backend: ""`                                                | **Raises** `ValueError("cli_backend string cannot be empty")`.                      |
| Dict missing `id`      | `cli_backend: {overrides: {...}}`                                | **Raises** `ValueError("cli_backend dict must contain an 'id' field")`.             |
| `overrides` not a dict | `cli_backend: {id: claude-code, overrides: "bad"}`               | **Raises** `ValueError("cli_backend.overrides must be a dict")`.                    |
| Invalid type           | `cli_backend: 123`                                               | **Raises** `ValueError("cli_backend must be string, dict, or instance, got: int")`. |
| Unknown id             | `cli_backend: nope`                                              | Returns `None` + logged warning. (Registry lookup, unchanged.)                      |

***

## Framework Compatibility

`cli_backend` is a runtime feature — it works only with an adapter whose `SUPPORTS_RUNTIME_FEATURES = True` (the built-in `praisonai` adapter has it).

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{adapter SUPPORTS_RUNTIME_FEATURES?}
    Q -->|True| OK[✅ cli_backend takes effect]
    Q -->|False| FAIL[❌ ValueError at config validation]
    
    classDef question fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef success fill:#10B981,stroke:#7C90A0,color:#fff
    classDef error fill:#8B0000,stroke:#7C90A0,color:#fff
    
    class Q question
    class OK success
    class FAIL error
```

| `framework` value                        | `cli_backend` behaviour                                                                                                                                                                                                                                           |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `praisonai` (default)                    | Resolved and used to delegate every agent turn.                                                                                                                                                                                                                   |
| `crewai`, `autogen`, `autogen_v4`, `ag2` | **Raises** `ValueError: Runtime features (...) are not supported for framework='<x>'. Use a framework whose adapter sets SUPPORTS_RUNTIME_FEATURES = True (e.g. framework='praisonai').` Validation runs before any agent starts, so no partial run is performed. |

Third-party adapters opt in by setting `SUPPORTS_RUNTIME_FEATURES = True` on their subclass — see [Capability Flags](/docs/docs/features/framework-adapter-plugins#capability-flags).

<Note>
  **YAML `cli_backend:` behavior changes (PR #4111).** The wrapper adapter now resolves `cli_backend:` via `_resolve_yaml_cli_backend` into the `cli_backend` kwarg core `Agent` expects, and the shipped `examples/yaml/cli_backend.yaml` works again. Two paths now fail loudly instead of silently misrouting:

  1. **`cli_backend:` in a `process: workflow` YAML is now rejected** — the workflow engine runs agents natively:

     ```
     cli_backend (declared by: <role>) is not supported for 'process: workflow' YAMLs; the workflow engine runs agents natively. Remove cli_backend or convert to a sequential/hierarchical process.
     ```

  2. **Unresolvable backend ids now fail closed** during wrapper resolution (previously they could silently misroute):

     ```
     Agent '<role>' requests cli_backend=<cfg> but it could not be resolved. Install praisonai-code and use an id from 'praisonai backends', or remove the cli_backend field.
     ```
</Note>

<Note>
  This is a behaviour change in PR #1797 — previously `cli_backend` was silently ignored under non-praisonai frameworks. Now it fails loudly at config-load time. A follow-up fix ([PR #2004](https://github.com/MervinPraison/PraisonAI/pull/2004)) restored this validation for `framework: praisonai` — previously a regression caused all `cli_backend` configs to fail at validation regardless of framework.
</Note>

***

## Using `cli_backend` in Python

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

# 1. String form — same as YAML
agent = Agent(
    name="Refactorer",
    instructions="Refactor Python files cleanly",
    cli_backend="claude-code",
)

agent.start("Refactor utils.py")
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 2. Dict form with overrides — now works in Python too (PR #1797)
agent = Agent(
    name="Refactorer",
    instructions="Refactor Python files cleanly",
    cli_backend={
        "id": "claude-code",
        "overrides": {"timeout_ms": 60000},
    },
)
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 3. Pre-resolved instance — full control
from praisonai.cli_backends import resolve_cli_backend

backend = resolve_cli_backend("claude-code", overrides={"timeout_ms": 60000})

agent = Agent(
    name="Refactorer",
    cli_backend=backend,
)
```

| Shape      | Example                                                         | When to use                              |
| ---------- | --------------------------------------------------------------- | ---------------------------------------- |
| `str`      | `cli_backend="claude-code"`                                     | Quickest path. Matches YAML idiom.       |
| `dict`     | `cli_backend={"id": "claude-code", "overrides": {...}}`         | Need timeout / args / model overrides.   |
| instance   | `cli_backend=resolve_cli_backend("claude-code", overrides=...)` | Pre-resolved once, reused across agents. |
| `callable` | `cli_backend=my_factory`                                        | Factory function returning a backend.    |

<Note>
  YAML and Python now accept the exact same shapes (was previously YAML-only for dict).
</Note>

***

## Built-in backends

Four backends ship registered out of the box. Each routes through a CLI that owns its own subscription/OAuth session, so no raw API key is needed.

| ID            | CLI          | Auth source          | Page                             |
| ------------- | ------------ | -------------------- | -------------------------------- |
| `claude-code` | `claude`     | Claude Pro / Max     | [Claude Code](/docs/code/claude-code) |
| `codex-cli`   | `codex exec` | ChatGPT subscription | [Codex CLI](/docs/code/codex-cli)     |
| `grok-cli`    | `grok -p`    | xAI subscription     | [Grok CLI](/docs/code/grok-cli)       |
| `gemini-cli`  | `gemini -p`  | Google account       | [Gemini CLI](/docs/code/gemini-cli)   |

Select any of them by ID — the shape is identical to `claude-code`:

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
cli_backend: codex-cli   # ChatGPT subscription
cli_backend: grok-cli    # xAI subscription
cli_backend: gemini-cli  # Google account
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(name="assistant", cli_backend="codex-cli")
agent.start("Hello")
```

<Note>
  `cli_backend=` is deprecated (removal in 2.0.0). Prefer the equivalent `runtime=` — see [Runtime Selection](/docs/features/runtime-selection).
</Note>

## Built-in Backend: `claude-code`

The `claude-code` backend executes commands via the Claude Code CLI with these default settings:

| Option               | Type             | Default                                                                                                                                                                                                                                                                                                                                                                                                | Description                                                              |
| -------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| `command`            | `str`            | `"claude"`                                                                                                                                                                                                                                                                                                                                                                                             | CLI command (must be on PATH)                                            |
| `args`               | `List[str]`      | `["-p", "--output-format", "stream-json", ..., "--permission-mode", "default"]`                                                                                                                                                                                                                                                                                                                        | Default permission mode is `default` (PR #2122; was `bypassPermissions`) |
| `resume_args`        | `List[str]`      | `["-p", "--output-format", "stream-json", "--resume", "{session_id}"]`                                                                                                                                                                                                                                                                                                                                 | Arguments for resuming sessions                                          |
| `output`             | `str`            | `"jsonl"`                                                                                                                                                                                                                                                                                                                                                                                              | Output format expected from CLI                                          |
| `input`              | `str`            | `"stdin"`                                                                                                                                                                                                                                                                                                                                                                                              | How to pass prompts to CLI                                               |
| `live_session`       | `str`            | `"claude-stdio"`                                                                                                                                                                                                                                                                                                                                                                                       | Live session mode                                                        |
| `model_arg`          | `str`            | `"--model"`                                                                                                                                                                                                                                                                                                                                                                                            | CLI argument for model selection                                         |
| `model_aliases`      | `Dict[str, str]` | `{"opus": "claude-opus-4-5", "sonnet": "claude-sonnet-4-5", "haiku": "claude-haiku-3-5"}`                                                                                                                                                                                                                                                                                                              | Model name shortcuts                                                     |
| `session_arg`        | `str`            | `"--session-id"`                                                                                                                                                                                                                                                                                                                                                                                       | CLI argument for session ID                                              |
| `session_mode`       | `str`            | `"always"`                                                                                                                                                                                                                                                                                                                                                                                             | When to use sessions                                                     |
| `session_id_fields`  | `List[str]`      | `["session_id"]`                                                                                                                                                                                                                                                                                                                                                                                       | Fields containing session ID                                             |
| `system_prompt_arg`  | `str`            | `"--append-system-prompt"`                                                                                                                                                                                                                                                                                                                                                                             | CLI argument for system prompts                                          |
| `system_prompt_when` | `str`            | `"first"`                                                                                                                                                                                                                                                                                                                                                                                              | When to add system prompts                                               |
| `image_arg`          | `str`            | `"--image"`                                                                                                                                                                                                                                                                                                                                                                                            | CLI argument for images                                                  |
| `clear_env`          | `List[str]`      | `["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_OAUTH_TOKEN", "CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX", "CLAUDE_CONFIG_DIR", "CLAUDE_CODE_OAUTH_TOKEN", "OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_EXPORTER_OTLP_HEADERS", "OTEL_RESOURCE_ATTRIBUTES", "GOOGLE_APPLICATION_CREDENTIALS", "AWS_PROFILE", "AWS_REGION", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]` | Environment variables sanitized before subprocess                        |
| `bundle_mcp`         | `bool`           | `True`                                                                                                                                                                                                                                                                                                                                                                                                 | Enable MCP bundling                                                      |
| `bundle_mcp_mode`    | `str`            | `"claude-config-file"`                                                                                                                                                                                                                                                                                                                                                                                 | MCP bundling mode                                                        |
| `serialize`          | `bool`           | `True`                                                                                                                                                                                                                                                                                                                                                                                                 | Queue operations to avoid conflicts                                      |
| `timeout_ms`         | `int`            | `300000`                                                                                                                                                                                                                                                                                                                                                                                               | Subprocess timeout (5 minutes)                                           |

***

## The `--cli-backend` CLI Flag

| Flag                                    | Type   | Behavior                                                                                                                   |
| --------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------- |
| `--cli-backend BACKEND_ID`              | string | Choices populated dynamically from `list_cli_backends()`. Currently: `claude-code`, `codex-cli`, `grok-cli`, `gemini-cli`. |
| `--cli-backend X --external-agent Y`    | —      | **Mutually exclusive** — argparse rejects with "not allowed with argument"                                                 |
| Unknown id (e.g. `--cli-backend bogus`) | —      | Rejected by argparse with "invalid choice"                                                                                 |

***

## The `backends` Subcommand

| Command                    | Behavior                                                                                 |
| -------------------------- | ---------------------------------------------------------------------------------------- |
| `praisonai backends list`  | Prints each registered backend id on its own line                                        |
| `praisonai backends`       | Same as `list` (list is the default)                                                     |
| `praisonai backends bogus` | Prints `[red]Unknown backends subcommand: bogus[/red]` and the list of valid subcommands |

***

## Custom Backends (Advanced)

Register your own CLI backend for custom tools:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.cli_backends import register_cli_backend
from praisonaiagents import CliBackendConfig

def my_backend_factory():
    from my_pkg import MyBackend
    return MyBackend(config=CliBackendConfig(command="my-cli"))

register_cli_backend("my-backend", my_backend_factory)
```

After registering, `praisonai backends list` shows it, `--cli-backend my-backend` accepts it, and `cli_backend: my-backend` works in YAML.

***

## CliBackendProtocol Reference

For backend authors implementing the protocol:

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "CliBackendProtocol"
        A[config: CliBackendConfig] 
        B[async execute(...)]
        C[async stream(...)]
        D[capabilities()]
    end
    
    classDef protocol fill:#8B0000,stroke:#7C90A0,color:#fff
    
    class A,B,C,D protocol
```

* `config: CliBackendConfig` — Configuration object
* `async def execute(prompt, *, session=None, images=None, system_prompt=None, **kwargs) -> CliBackendResult` — Single execution
* `async def stream(prompt, **kwargs) -> AsyncIterator[CliBackendDelta]` — Streaming execution
* `def capabilities() -> RuntimeCapabilityMatrix` — **Required (new).** Returns the capability matrix for this backend.

<Note>
  **Breaking change:** `CliBackendProtocol` now requires a `capabilities() -> RuntimeCapabilityMatrix` method so the framework can validate capabilities at config time. Third-party backends must add this method. Without it, the backend will be treated as supporting only the reduced capability set (`tool_loop`, `basic_chat`, `simple_tools`).
</Note>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Prefer YAML for production">
    Use the YAML `cli_backend:` field for versioned, declarative configuration. Use `--cli-backend` flag for quick one-off commands and testing.
  </Accordion>

  <Accordion title="Set timeout overrides for slow CLIs">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    cli_backend:
      id: claude-code
      overrides:
        timeout_ms: 60000  # 1 minute instead of 5
    ```

    Rather than monkey-patching, use the overrides system for custom timeouts.
  </Accordion>

  <Accordion title="Use a framework whose adapter sets SUPPORTS_RUNTIME_FEATURES">
    The `cli_backend` field requires an adapter whose `SUPPORTS_RUNTIME_FEATURES = True` — the built-in `framework: praisonai` (the default) qualifies. PraisonAI validates this up front — if you try it with `crewai`, `autogen`, `autogen_v4`, or `ag2`, you'll get a `ValueError` immediately, before any agent runs.
  </Accordion>

  <Accordion title="Don't combine with --external-agent">
    The `--cli-backend` flag and `--external-agent` flag are mutually exclusive. Pick one approach:

    * CLI Backends (new): Pluggable, configurable, YAML-supported
    * External Agent (legacy): Class-based, limited configuration
  </Accordion>

  <Accordion title="Ensure claude CLI is on PATH">
    The `claude-code` backend requires the `claude` CLI to be installed and accessible. Install via the Claude Code SDK or ensure it's in your system PATH.
  </Accordion>
</AccordionGroup>

***

## How this differs from `--external-agent`

<Note>
  The legacy `--external-agent claude` and `ClaudeCodeIntegration` class still work and are unchanged (see [External CLI Integrations](/docs/features/external-cli-integrations)). The CLI Backend Protocol is the **new pluggable** path: backends are registered by id, configured declaratively, and surfaced as a YAML field and `--cli-backend` flag.
</Note>

***

## Related

<CardGroup cols={2}>
  <Card title="Runtime Selection" icon="play" href="/docs/features/runtime-selection">
    Model-scoped runtime configuration (replaces cli\_backend)
  </Card>

  <Card title="External CLI Integrations" icon="link" href="/docs/features/external-cli-integrations">
    Legacy class-based CLI integration approach
  </Card>

  <Card title="Agent Configuration" icon="gear" href="/docs/features/agent-profiles">
    Core Agent configuration and usage patterns
  </Card>
</CardGroup>
