Skip to content

Identity profiles and runtime

A named identity profile is a config directory — .gsloth/.gsloth-settings/<name>/ — that carries its own llm block, prompt files, and tool selection. Selecting one with -i <name> swaps the entire project-file config layer for that directory, so one team, one task, or one model can be a single-flag switch with nothing to edit between runs. This page is the reference for profiles, for handing a sub-task to a subagent running under a different profile (subagents), and for the two runtime knobs that sit alongside them — the AG-UI server config and the agent.backend selector.

For the model-switching recipe (a cheap profile for questions, a strong one for review), see Choose and switch models; this page is the deep reference it points to.

The main use case: a DevOps identity with its own model, guidelines, and review prompt

Section titled “The main use case: a DevOps identity with its own model, guidelines, and review prompt”

Goal: your DevOps team reviews PRs against infra/security concerns with their own model and their own review checklist, while developers keep the default project config — no shared config to fight over.

Scaffold a profile, seeding it with the model DevOps wants:

Terminal window
gth config profile create devops --model claude-sonnet-4-5

That writes .gsloth/.gsloth-settings/devops/.gsloth.config.json. Drop the DevOps-specific prompt files next to it in the same directory — an infra/security .gsloth.guidelines.md and a .gsloth.review.md review checklist:

.gsloth/.gsloth-settings/devops/.gsloth.config.json
.gsloth/.gsloth-settings/devops/.gsloth.guidelines.md
.gsloth/.gsloth-settings/devops/.gsloth.review.md

Now review a PR under that identity:

Terminal window
gth -i devops pr 42

The config, guidelines, and review prompt all come from the devops/ directory instead of the project defaults. Any prompt file you didn’t create in the profile falls back to the installation default, so you only author the ones DevOps needs to differ on. Run gth pr 42 without -i and the config resolves from the default .gsloth/.gsloth-settings/ directory as usual — developers are unaffected.

Sometimes two different teams have different perspectives of a project. For example, developers may want to review the code for code quality; DevOps may want to be notified when some configuration files or docker image change. Their configurations of Gaunt Sloth may be so different that it is better to keep them in complete separation. Identity profiles define different Gaunt Sloth identities for different purposes.

Identity profiles can only be activated in directory-based configuration. When gth -i devops pr PR_NO is invoked, the configuration is pulled from the .gsloth/.gsloth-settings/devops/ directory, which may contain a full set of config files:

.gsloth.backstory.md
.gsloth.config.json
.gsloth.guidelines.md
.gsloth.review.md

When no identity profile is specified in the command, for example gth pr PR_NO, the configuration is pulled from the .gsloth/.gsloth-settings/ directory.

-i / --identity-profile (or its alias --profile) overrides the entire configuration directory, which means it should contain a configuration file and prompt files. In the case where some prompt files are missing, they will be fetched from the installation directory. (The individual prompt files — backstory, guidelines, system, review, and the per-mode prompts — are resolved from the selected profile directory first.)

Precedence. A selected profile replaces the project-file layer of the config cascade with the profile directory’s config; everything else stacks as usual:

explicit CLI flags (-c, --model, --verbose, -w) > profile-dir config > global ~/.gsloth config > built-in defaults

So a profile is the highest-precedence file layer, still overridable by explicit command-line flags, and still sitting on top of your global ~/.gsloth config and the built-in defaults. Naming a profile that has no config file of its own is an error: the run stops rather than silently falling back to some other config — not to your global ~/.gsloth config, and not to the project’s own plain .gsloth.config.* either, which is the fallback you would otherwise get and the surprising one. A mistyped -i therefore tells you so, instead of running under a model you did not choose.

Where a profile is looked up. By default, in the project: .gsloth/.gsloth-settings/<name>/, searched from the working directory up to the repository root. Add -g/--global and the same name is looked up in ~/.gsloth/.gsloth-settings/<name>/ instead — your own profiles, in a repository that knows nothing about them. The two are separate namespaces: a profile that exists only in the project is not found under -g, and vice versa.

Two ways to create a profile, depending on how you want it seeded:

  • gth init -i <name> walks the same CFG-2 provider/model dialog as a plain gth init, then writes into the named profile instead of the unscoped config. Use this when you want to pick the provider and model interactively (from the live catalog, preferred models starred) rather than seed from something that already exists. Add -g to create the profile under ~/.gsloth/.gsloth-settings/<name>/ instead of the project:

    Terminal window
    gth init -i test2 # .gsloth/.gsloth-settings/test2/.gsloth.config.json
    gth init -g -i test2 # ~/.gsloth/.gsloth-settings/test2/.gsloth.config.json

    gth init -i <name> <provider> (the scriptable path, e.g. gth init -i test2 anthropic) skips the dialog and writes the project profile directly; add -g to target the global profile instead.

  • gth config profile create <name> scaffolds a new profile directory (.gsloth/.gsloth-settings/<name>/.gsloth.config.json), seeded from your current effective config (or a minimal template when none resolves) and schema-validated before it is written. Pass --model <id> to set the profile’s model, and --force to overwrite an existing profile. Use this when the new profile should start as a copy of what you already have.

    For example, to add a cheap flash-lite profile alongside your normal setup and then run under it:

    Terminal window
    gth config profile create cheap --model gemini-2.0-flash-lite
    gth --profile cheap ask "summarise the open TODOs in this repo"

Either way you end up with an ordinary config file at .gsloth/.gsloth-settings/<name>/.gsloth.config.json (or its global counterpart) that you can go on to edit — adjust its tools, prompts, or provider as needed.

subagents lets the agent delegate a sub-task to a subagent that runs under a different named profile — its own model, tools, and prompt — instead of the parent’s. The typical use: keep the parent on a strong (expensive) model but hand routine search/recall work to a cheap one, so the bulk of the tokens are spent on the cheap model.

Each entry names a subagent (the name the model selects it by) and the profile the child resolves:

{
"llm": { "type": "anthropic", "model": "claude-opus-4-1" },
"subagents": [
{ "name": "recall", "description": "Cheap read-only search/recall.", "profile": "cheap" }
]
}

To make the example above run, create the cheap profile it references, then start a coding session — when the model delegates a recall task, that subagent runs on gemini-2.0-flash-lite, not on the parent’s claude-opus-4-1:

Terminal window
gth config profile create cheap --model gemini-2.0-flash-lite
gth code

The child resolves the named profile through the same config cascade a top-level --profile run does, so it picks up that profile’s model, tool selection, and prompt files. A subagent whose profile has no config directory is an error, exactly as selecting a missing profile with --profile is.

Not dispatched yet. No agent backend spawns subagents at present, so a declared subagents block has no effect on a run. It stays a valid config key rather than an error, and a run that declares one prints a warning naming the subagents it did not use — so the setting is announced, never quietly ignored. Write the block now if you want it in place; expect no delegation until subagent dispatch ships.

The api ag-ui command reads its settings from commands.api in your config file.

Field Type Default Description
commands.api.port number 3000 Port the AG-UI server listens on
commands.api.cors.allowOrigin string "http://localhost:3000" Access-Control-Allow-Origin header value
commands.api.cors.allowMethods string "POST, GET, OPTIONS" Access-Control-Allow-Methods header value
commands.api.cors.allowHeaders string "Content-Type, Accept" Access-Control-Allow-Headers header value

Example config for the Galvanized Pukeko web client on port 5555:

{
"llm": {
"type": "anthropic",
"model": "claude-sonnet-4-5"
},
"commands": {
"api": {
"port": 3000,
"cors": {
"allowOrigin": "http://localhost:5555",
"allowMethods": "POST, GET, OPTIONS",
"allowHeaders": "Content-Type, Accept"
}
}
}
}

Note: The port flag --port on the CLI overrides commands.api.port.

Gaunt Sloth ships one agent backend, lean — a plain LangChain agent carrying Gaunt Sloth’s own toolset: filesystem, hardened dev/shell, and the gth_checklist planning tool. It is what every command runs, and what runs when the key is absent, so setting it changes nothing:

{
"llm": { "type": "anthropic", "model": "claude-sonnet-4-5" },
"agent": { "backend": "lean" }
}

"lean" is the only accepted value. The key is kept so a config can state what it runs on, and so a second backend has a name to be selected by; a value that is not "lean" is a config error rather than a silently ignored key.

A config carrying the retired "backend": "deep" fails to load, with a message naming the replacement — see Migrating to 2.0 for what to change and what it costs you.