Navigation
Copy page

Profiles

On this page

A profile is a named group of extensions kli boots with.

A profile is a named group of extensions

kli is a small kernel with everything else installed on top as an extension: the model providers, the tools, the commands, the terminal UI. A profile is the list that says which of those to install at boot, under one name.

The list is built from groups of extension manifests. Three carry the substance:

  • A baseline group every profile installs. It holds the parts kli needs to be an agent at all: the event system, the session log, the config layer, the agent loop, and the file, search, and shell tools.
  • A model-provider group: the anthropic, openai, openai-codex, and compatible providers. A profile that talks to a model installs this; one that does not, leaves it out.
  • A terminal-UI group: the chat view, the input editor, markdown rendering, the slash commands, completion. Only a profile meant to be driven by a person at a terminal installs this.

Every profile also carries a nix-declared group, spliced in right after the baseline. It is empty in plain kli and holds whatever a Nix-configured image declares at boot, so those extensions boot as baseline children. The profiles reference lists exactly what each group installs.

A profile names the groups it wants and they install together as a unit. A profile is nothing more than that explicit list, which is why the built-ins differ only in which groups they include. The kernel itself never learns what a profile is; it sees the install requests and nothing more. For why installing and retracting extensions on a running kernel works at all, see The Live Image.

The four built-in profiles

Each built-in is a fixed combination of those groups, plus a declaration of any capabilities the profile expects you to supply.

  • interactive-terminal installs the baseline, model-provider, and terminal-UI groups. This is the default — what you get when you run kli with no profile selected. It is the profile for sitting at a terminal and working with the agent.
  • headless installs the baseline group only. No model providers, no terminal UI. It is the minimal agent core, the starting point for a profile or an embedding that wires its own providers and front end.
  • human-in-loop installs the same groups as interactive-terminal and declares an approval seam — a named point a human-approval extension is meant to fill. The profile itself does not provide approval; it states that the slot exists so an extension can complete it.
  • autonomous installs the baseline and the model providers but not the terminal UI, and declares planner, scheduler, watchdog, and recovery seams. It is the shape for an agent that runs without a person watching: it can talk to a model and use tools, with the supervision pieces left as seams for you to fill.

A seam is a capability a profile declares but does not provide. It is how a profile names its own extension points without pretending to satisfy them. What fills a seam is a separate extension, governed by the capabilities array and your installed extensions.

How precedence picks the active profile

At boot kli resolves one profile name from the first of these that is set:

  1. The --profile <name> command-line flag.
  2. The KLI_PROFILE environment variable.
  3. The profile key in your merged settings, where a project's <repo>/.kli/settings.json wins over the global ~/.config/kli/settings.json.
  4. The default, interactive-terminal, when none of the above names a profile.

The flag wins over the environment variable, which wins over settings, which wins over the default. If the resolved name is neither a built-in nor a profile declared in settings, kli warns and boots the default rather than failing. The warning surfaces in the session, so a typo in a profile name does not leave you guessing why you got the terminal you did not ask for.

Profiles you define in settings

Beyond the four built-ins you can declare your own profile under the profiles object in settings.json. A declared profile is a delta on top of a built-in, with four fields:

  • extends — the profile it builds on. With no extends, it bottoms out at interactive-terminal.
  • enable — extension ids to add to the active set.
  • disable — extension ids to remove from it.
  • settings — a settings overlay that rides along while the profile is active.

Resolution walks the extends chain down to a built-in base, then folds the deltas from the base up: a later profile in the chain has the last word on any given extension id, and its settings merge over the earlier ones. A declared profile cannot reuse a built-in name; that is reserved, and an entry that tries to shadow one is ignored with a warning. A malformed entry is skipped the same way, so one bad profile does not stop the rest from loading.

The enable and disable deltas gate your user extensions, the optional ones you install yourself. They do not gate the built-in groups, which arrive with the base. Whether a given user extension is installed at boot comes down to its own configuration and the active profile's deltas together.

Switching profile while kli runs

The /profile command lists the available profiles and live-switches between them. A switch re-bases your user extensions onto the target profile's set — installing the ones it wants that are absent, retracting the ones it does not — and swaps in the target's settings overlay. Because the switch operates on a running kernel, your context and session log carry across it.

One thing does not switch live: the built-in base. The base group is installed once at boot, so switching to a profile with a different base (from interactive-terminal to headless, say) cannot take effect in place. kli tells you so and points you at restarting with --profile <name>. Everything that is a delta on the same base switches in the running session.