# Profiles


A profile is a named, ordered group of extension manifests that kli installs as a
unit at boot. It determines which extensions are present before any user
extension loads: whether the terminal UI is built, which model providers are
available, and which tools and commands exist. kli boots exactly one profile.

The kernel never learns about profiles. A profile installs through the same
manifest primitives as any other extension group, and its activation record
lives in per-protocol storage.

## Built-in profiles

kli ships four built-in profiles. Each composes one or more of the manifest
groups below and may declare seams (see [Seams](#seams)).

| Profile | Manifest groups | Declared seams | Terminal UI |
|---|---|---|---|
| `interactive-terminal` | baseline, nix-declared, model-provider, tui-app | — | yes |
| `human-in-loop` | baseline, nix-declared, model-provider, tui-app | `approval` | yes |
| `autonomous` | baseline, nix-declared, model-provider | `planner`, `scheduler`, `watchdog`, `recovery` | no |
| `headless` | baseline, nix-declared | — | no |

`interactive-terminal` is the default. A profile that omits the `tui-app` group
builds no TUI and runs no terminal loop; boot holds the main thread instead, and
boot diagnostics print to standard error rather than into a transcript.

### interactive-terminal

The default profile. Installs the baseline, the model providers, and the
terminal-UI extensions. This is the full interactive agent: a TUI with the
transcript, input editor, status widgets, completion, slash commands, and the
session bound for chat on boot. Declares no seam.

### human-in-loop

The same manifest groups as `interactive-terminal` — baseline, model providers,
and the terminal UI — and additionally declares the `approval` seam. The
`approval` seam is declared, not implemented: the profile names the extension
point but supplies nothing for it, and kli ships no `approval` provider. Booting
`human-in-loop` today gives you the same running extensions as
`interactive-terminal`. The seam marks where an approval extension would attach.

### autonomous

Installs the baseline and the model providers, and omits the `tui-app` group, so
it builds no terminal UI. Declares four seams: `planner`, `scheduler`,
`watchdog`, and `recovery`. As with `approval`, these are declared extension
points, not implemented behavior; kli ships no provider for any of them. Booting
`autonomous` brings up the agent substrate without a TUI and holds the main
thread.

### headless

The baseline group only: no model providers and no terminal UI. The minimal
substrate. Provider and UI extensions are absent, so the session is not bound for
chat on boot. The agent substrate stays alive without a terminal loop.

## Manifest groups

The groups each built-in profile composes from.

| Group | Contents |
|---|---|
| baseline | The kernel-facing substrate present in every profile: object and event extensions, the command and config services, the session log, the context lens, auth and OAuth, the model registry and runtime, the agent loop and session, the `eval`, `bash`, read, write, edit, find, and search tools, the filesystem-anchor lifecycle, and the runtime control, journal, snapshot, history, and introspection extensions. |
| nix-declared | Extensions a Nix-configured image declares present at boot. Empty in plain kli; only a configured image's boot shim populates it. Each profile splices it in directly after the baseline, so declared extensions boot as baseline children. |
| model-provider | The four model providers: `openai-codex`, `openai`, `anthropic`, and `compatible`. See [Providers](/kli/concepts/models-providers-and-transports). |
| tui-app | The terminal UI and everything bound to it: style, keymap, status, views, input, editor, terminal, markdown, transcript, and the TUI app itself; the basic and session commands; the `eval`, `bash`, and model commands; config commands; prompt templates; context-file commands; skills; completion; settings wiring; and observability. |

## Seams

A seam is a capability a profile declares as an extension point but does not
provide itself. It names where a user (or a future extension) is expected to
attach a provider to complete the profile. A profile carries its declared seams
in its activation record; declaring a seam installs nothing and runs nothing.

No built-in seam has an implementation in kli today. The `approval` seam declared
by `human-in-loop` and the `planner`, `scheduler`, `watchdog`, and `recovery`
seams declared by `autonomous` are markers only. In particular, `human-in-loop`
does not gate tool execution behind an approval step: kli is
[fully permissioned by default](/kli/concepts/capabilities-and-fault-barriers), and no profile
changes that.

## Selection precedence

kli resolves the boot profile name from the first source that supplies one, in
this order:

1. The `--profile <name>` command-line flag.
2. The `KLI_PROFILE` environment variable, when set and non-empty.
3. The `profile` key in settings (a non-empty string). The merged settings put
   the project file over the global file, so a project `.kli/settings.json` value
   overrides the one in `~/.config/kli/settings.json`.
4. The default, `interactive-terminal`.

A name that resolves to neither a built-in profile nor a declared data profile
warns and falls back to `interactive-terminal`. The warning surfaces as a boot
diagnostic.

The name is matched case- and separator-insensitively (normalized to a keyword),
so `human-in-loop` and `HUMAN_IN_LOOP` name the same profile.

## Custom data profiles

A data profile is a profile declared as data in settings rather than as a
built-in. It lives under a `profiles` object in `settings.json`, keyed by
profile name:

```json
{
  "profiles": {
    "review": {
      "extends": "interactive-terminal",
      "enable": ["my-review-helper"],
      "disable": ["some-noisy-extension"],
      "settings": {
        "model": "anthropic/claude-..."
      }
    }
  }
}
```

Each entry accepts these fields. All are optional.

| Field | Type | Effect |
|---|---|---|
| `extends` | string | Name of the profile this one builds on: another data profile or a built-in. The extends chain is walked until it bottoms at a built-in, which supplies the manifest groups. An entry with no `extends` bottoms at `interactive-terminal`. |
| `enable` | array of strings | User-extension ids to add to the effective set. A later entry in the chain has the last word over an earlier one for a given id. |
| `disable` | array of strings | User-extension ids to remove from the effective set. `enable` and `disable` for the same id resolve to whichever the chain states last. |
| `settings` | object | A settings overlay recorded for this profile. It merges over the chain's earlier overlays and is applied as the topmost settings layer (`global < project < overlay`) when the profile is active. |

A data profile is resolved into a base built-in plus effective `enable` and
`disable` delta sets and a merged settings overlay. The base built-in's manifest
groups install at boot; the deltas then gate which user extensions load on top.
Data profiles change the user-extension set and settings, not the built-in
manifest groups: a data profile cannot add a manifest group its base does not
already include.

Resolution is fail-soft. A malformed entry warns and is skipped. An entry whose
name collides with a built-in profile warns and is skipped — built-in names
cannot be shadowed. An unknown `extends` target or a cyclic `extends` chain warns
and the profile fails to resolve. Each warning surfaces as a boot diagnostic.

## Live switching

While kli is running, `/profile` with no argument lists the available profiles
with the active one marked, and `/profile <name>` switches to a named profile by
re-basing the user-extension pool onto the target's effective set and swapping
its settings overlay. A switch that would change the built-in base does not take
effect live; the base applies only at boot, and kli tells you to restart with
`--profile <name>`. See the [CLI reference](/kli/cli) for boot-time selection.
