Skip to content

Commit b04c838

Browse files
committed
feat!: redesign model config + auth profiles
1 parent bd2e003 commit b04c838

60 files changed

Lines changed: 2025 additions & 778 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### Breaking
88
- Timestamps in agent envelopes are now UTC (compact `YYYY-MM-DDTHH:mmZ`); removed `messages.timestampPrefix`. Add `agent.userTimezone` to tell the model the user’s local time (system prompt only).
9+
- Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup.
910

1011
### Fixes
1112
- Onboarding: resolve CLI entrypoint when running via `npx` so gateway daemon install works without a build step.
@@ -16,10 +17,10 @@
1617
- macOS: local gateway now connects via tailnet IP when bind mode is `tailnet`/`auto`.
1718
- macOS: Connections settings now use a custom sidebar to avoid toolbar toggle issues, with rounded styling and full-width row hit targets.
1819
- macOS: drop deprecated `afterMs` from agent wait params to match gateway schema.
19-
- Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth.json.
20+
- Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth-profiles.json.
2021
- Model: `/model` list shows auth source (masked key or OAuth email) per provider.
2122
- Model: `/model list` is an alias for `/model`.
22-
- Model: `/model` output now includes auth source location (env/auth.json/models.json).
23+
- Model: `/model` output now includes auth source location (env/auth-profiles.json/models.json).
2324
- Model: avoid duplicate `missing (missing)` auth labels in `/model` list output.
2425
- Docs: clarify auth storage, migration, and OpenAI Codex OAuth onboarding.
2526
- Sandbox: copy inbound media into sandbox workspaces so agent tools can read attachments.

docs/configuration.md

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,40 @@ Env var equivalent:
9191

9292
### Auth storage (OAuth + API keys)
9393

94-
Clawdbot stores **OAuth credentials** in:
94+
Clawdbot stores **auth profiles** (OAuth + API keys) in:
95+
- `~/.clawdbot/agent/auth-profiles.json`
96+
97+
Legacy OAuth imports:
9598
- `~/.clawdbot/credentials/oauth.json` (or `$CLAWDBOT_STATE_DIR/credentials/oauth.json`)
9699

97-
Clawdbot stores **API keys** in the agent auth store:
98-
- `~/.clawdbot/agent/auth.json`
100+
The embedded Pi agent maintains a runtime cache at:
101+
- `~/.clawdbot/agent/auth.json` (managed automatically; don’t edit manually)
99102

100103
Overrides:
101-
- OAuth dir: `CLAWDBOT_OAUTH_DIR`
104+
- OAuth dir (legacy import only): `CLAWDBOT_OAUTH_DIR`
102105
- Agent dir: `CLAWDBOT_AGENT_DIR` (preferred), `PI_CODING_AGENT_DIR` (legacy)
103106

104-
On first use, Clawdbot imports `oauth.json` entries into `auth.json` so the embedded
105-
agent can use them. `oauth.json` remains the source of truth for OAuth refresh.
107+
On first use, Clawdbot imports `oauth.json` entries into `auth-profiles.json`.
108+
109+
### `auth`
110+
111+
Optional metadata for auth profiles. This does **not** store secrets; it maps
112+
profile IDs to a provider + mode (and optional email) and defines the provider
113+
rotation order used for failover.
114+
115+
```json5
116+
{
117+
auth: {
118+
profiles: {
119+
"anthropic:default": { provider: "anthropic", mode: "oauth", email: "me@example.com" },
120+
"anthropic:work": { provider: "anthropic", mode: "api_key" }
121+
},
122+
order: {
123+
anthropic: ["anthropic:default", "anthropic:work"]
124+
}
125+
}
126+
}
127+
```
106128

107129
### `identity`
108130

@@ -494,14 +516,12 @@ Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to `ELEVENLABS_V
494516
### `agent`
495517

496518
Controls the embedded agent runtime (model/thinking/verbose/timeouts).
497-
`allowedModels` lets `/model` list/filter and enforce a per-session allowlist
498-
(omit to show the full catalog).
499-
`modelAliases` adds short names for `/model` (alias -> provider/model).
500-
`modelFallbacks` lists ordered fallback models to try when the default fails.
501-
`imageModel` selects an image-capable model for the `image` tool.
502-
`imageModelFallbacks` lists ordered fallback image models for the `image` tool.
519+
`agent.models` defines the configured model catalog (and acts as the allowlist for `/model`).
520+
`agent.model.primary` sets the default model; `agent.model.fallbacks` are global failovers.
521+
`agent.imageModel` is optional and is **only used if the primary model lacks image input**.
503522

504-
Clawdbot also ships a few built-in `modelAliases` shorthands (when an `agent` section exists):
523+
Clawdbot also ships a few built-in alias shorthands. Defaults only apply when the model
524+
is already present in `agent.models`:
505525

506526
- `opus` -> `anthropic/claude-opus-4-5`
507527
- `sonnet` -> `anthropic/claude-sonnet-4-5`
@@ -515,23 +535,24 @@ If you configure the same alias name (case-insensitive) yourself, your value win
515535
```json5
516536
{
517537
agent: {
518-
model: "anthropic/claude-opus-4-5",
519-
allowedModels: [
520-
"anthropic/claude-opus-4-5",
521-
"anthropic/claude-sonnet-4-1"
522-
],
523-
modelAliases: {
524-
Opus: "anthropic/claude-opus-4-5",
525-
Sonnet: "anthropic/claude-sonnet-4-1"
538+
models: {
539+
"anthropic/claude-opus-4-5": { alias: "Opus" },
540+
"anthropic/claude-sonnet-4-1": { alias: "Sonnet" },
541+
"openrouter/deepseek/deepseek-r1:free": {}
542+
},
543+
model: {
544+
primary: "anthropic/claude-opus-4-5",
545+
fallbacks: [
546+
"openrouter/deepseek/deepseek-r1:free",
547+
"openrouter/meta-llama/llama-3.3-70b-instruct:free"
548+
]
549+
},
550+
imageModel: {
551+
primary: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
552+
fallbacks: [
553+
"openrouter/google/gemini-2.0-flash-vision:free"
554+
]
526555
},
527-
modelFallbacks: [
528-
"openrouter/deepseek/deepseek-r1:free",
529-
"openrouter/meta-llama/llama-3.3-70b-instruct:free"
530-
],
531-
imageModel: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
532-
imageModelFallbacks: [
533-
"openrouter/google/gemini-2.0-flash-vision:free"
534-
],
535556
thinkingDefault: "low",
536557
verboseDefault: "off",
537558
elevatedDefault: "on",
@@ -566,8 +587,8 @@ Block streaming:
566587
}
567588
```
568589

569-
`agent.model` should be set as `provider/model` (e.g. `anthropic/claude-opus-4-5`).
570-
If `modelAliases` is configured, you may also use the alias key (e.g. `Opus`).
590+
`agent.model.primary` should be set as `provider/model` (e.g. `anthropic/claude-opus-4-5`).
591+
Aliases come from `agent.models.*.alias` (e.g. `Opus`).
571592
If you omit the provider, CLAWDBOT currently assumes `anthropic` as a temporary
572593
deprecation fallback.
573594
Z.AI models are available as `zai/<model>` (e.g. `zai/glm-4.7`) and require
@@ -729,11 +750,16 @@ When `models.providers` is present, Clawdbot writes/merges a `models.json` into
729750
- default behavior: **merge** (keeps existing providers, overrides on name)
730751
- set `models.mode: "replace"` to overwrite the file contents
731752

732-
Select the model via `agent.model` (provider/model).
753+
Select the model via `agent.model.primary` (provider/model).
733754

734755
```json5
735756
{
736-
agent: { model: "custom-proxy/llama-3.1-8b" },
757+
agent: {
758+
model: { primary: "custom-proxy/llama-3.1-8b" },
759+
models: {
760+
"custom-proxy/llama-3.1-8b": {}
761+
}
762+
},
737763
models: {
738764
mode: "merge",
739765
providers: {
@@ -766,14 +792,10 @@ via **LM Studio** using the **Responses API**.
766792
```json5
767793
{
768794
agent: {
769-
model: "Minimax",
770-
allowedModels: [
771-
"anthropic/claude-opus-4-5",
772-
"lmstudio/minimax-m2.1-gs32"
773-
],
774-
modelAliases: {
775-
Opus: "anthropic/claude-opus-4-5",
776-
Minimax: "lmstudio/minimax-m2.1-gs32"
795+
model: { primary: "lmstudio/minimax-m2.1-gs32" },
796+
models: {
797+
"anthropic/claude-opus-4-5": { alias: "Opus" },
798+
"lmstudio/minimax-m2.1-gs32": { alias: "Minimax" }
777799
}
778800
},
779801
models: {

docs/doctor.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ Doctor will:
2727
- Show the migration it applied.
2828
- Rewrite `~/.clawdbot/clawdbot.json` with the updated schema.
2929

30+
The Gateway also auto-runs doctor migrations on startup when it detects a legacy
31+
config format, so stale configs are repaired without manual intervention.
32+
3033
Current migrations:
3134
- `routing.allowFrom``whatsapp.allowFrom`
35+
- `agent.model`/`allowedModels`/`modelAliases`/`modelFallbacks`/`imageModelFallbacks`
36+
`agent.models` + `agent.model.primary/fallbacks` + `agent.imageModel.primary/fallbacks`
3237

3338
## Usage
3439

docs/faq.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ Everything lives under `~/.clawdbot/`:
1515
|------|---------|
1616
| `~/.clawdbot/clawdbot.json` | Main config (JSON5) |
1717
| `~/.clawdbot/credentials/oauth.json` | OAuth credentials (Anthropic/OpenAI, etc.) |
18-
| `~/.clawdbot/agent/auth.json` | API key store |
18+
| `~/.clawdbot/agent/auth-profiles.json` | Auth profiles (OAuth + API keys) |
19+
| `~/.clawdbot/agent/auth.json` | Runtime API key cache (managed automatically) |
1920
| `~/.clawdbot/credentials/` | WhatsApp/Telegram auth tokens |
2021
| `~/.clawdbot/sessions/` | Conversation history & state |
2122
| `~/.clawdbot/sessions/sessions.json` | Session metadata |
@@ -576,21 +577,16 @@ List available models with `/model`, `/model list`, or `/model status`.
576577
Clawdbot ships a few default model shorthands (you can override them in config):
577578
`opus`, `sonnet`, `gpt`, `gpt-mini`, `gemini`, `gemini-flash`.
578579

579-
**Setup:** Configure allowed models and aliases in `clawdbot.json`:
580+
**Setup:** Configure models and aliases in `clawdbot.json`:
580581

581582
```json
582583
{
583584
"agent": {
584-
"model": "anthropic/claude-opus-4-5",
585-
"allowedModels": [
586-
"anthropic/claude-opus-4-5",
587-
"anthropic/claude-sonnet-4-5",
588-
"anthropic/claude-haiku-4-5"
589-
],
590-
"modelAliases": {
591-
"opus": "anthropic/claude-opus-4-5",
592-
"sonnet": "anthropic/claude-sonnet-4-5",
593-
"haiku": "anthropic/claude-haiku-4-5"
585+
"model": { "primary": "anthropic/claude-opus-4-5" },
586+
"models": {
587+
"anthropic/claude-opus-4-5": { "alias": "opus" },
588+
"anthropic/claude-sonnet-4-5": { "alias": "sonnet" },
589+
"anthropic/claude-haiku-4-5": { "alias": "haiku" }
594590
}
595591
}
596592
}
@@ -606,7 +602,8 @@ If you don't want to use Anthropic directly, you can use alternative providers:
606602
```json5
607603
{
608604
agent: {
609-
model: "openrouter/anthropic/claude-sonnet-4",
605+
model: { primary: "openrouter/anthropic/claude-sonnet-4" },
606+
models: { "openrouter/anthropic/claude-sonnet-4": {} },
610607
env: { OPENROUTER_API_KEY: "sk-or-..." }
611608
}
612609
}
@@ -616,7 +613,8 @@ If you don't want to use Anthropic directly, you can use alternative providers:
616613
```json5
617614
{
618615
agent: {
619-
model: "zai/glm-4.7",
616+
model: { primary: "zai/glm-4.7" },
617+
models: { "zai/glm-4.7": {} },
620618
env: { ZAI_API_KEY: "..." }
621619
}
622620
}

docs/models.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,32 @@ that prefers tool-call + image-capable models and maintains ordered fallbacks.
1616
- default: configured models only
1717
- flags: `--all` (full catalog), `--local`, `--provider <name>`, `--json`, `--plain`
1818
- `clawdbot models status`
19-
- show default model + aliases + fallbacks + allowlist
19+
- show default model + aliases + fallbacks + configured models
2020
- `clawdbot models set <modelOrAlias>`
21-
- writes `agent.model` in config
21+
- writes `agent.model.primary` and ensures `agent.models` entry
2222
- `clawdbot models set-image <modelOrAlias>`
23-
- writes `agent.imageModel` in config
23+
- writes `agent.imageModel.primary` and ensures `agent.models` entry
2424
- `clawdbot models aliases list|add|remove`
25-
- writes `agent.modelAliases`
25+
- writes `agent.models.*.alias`
2626
- `clawdbot models fallbacks list|add|remove|clear`
27-
- writes `agent.modelFallbacks`
27+
- writes `agent.model.fallbacks`
2828
- `clawdbot models image-fallbacks list|add|remove|clear`
29-
- writes `agent.imageModelFallbacks`
29+
- writes `agent.imageModel.fallbacks`
3030
- `clawdbot models scan`
3131
- OpenRouter :free scan; probe tool-call + image; interactive selection
3232

3333
## Config changes
3434

35-
- Add `agent.modelFallbacks: string[]` (ordered list of provider/model IDs).
36-
- Add `agent.imageModel?: string` (optional image-capable model for image tool).
37-
- Add `agent.imageModelFallbacks?: string[]` (ordered list for image tool).
38-
- Keep existing:
39-
- `agent.model` (default)
40-
- `agent.allowedModels` (list filter)
41-
- `agent.modelAliases` (shortcut names)
35+
- `agent.models` (configured model catalog + aliases).
36+
- `agent.model.primary` + `agent.model.fallbacks`.
37+
- `agent.imageModel.primary` + `agent.imageModel.fallbacks` (optional).
38+
- `auth.profiles` + `auth.order` for per-provider auth failover.
4239

4340
## Scan behavior (models scan)
4441

4542
Input
4643
- OpenRouter `/models` list (filter `:free`)
47-
- Requires `OPENROUTER_API_KEY` (or stored OpenRouter key in auth storage)
44+
- Requires OpenRouter API key from auth profiles or `OPENROUTER_API_KEY`
4845
- Optional filters: `--max-age-days`, `--min-params`, `--provider`, `--max-candidates`
4946
- Probe controls: `--timeout`, `--concurrency`
5047

@@ -66,17 +63,20 @@ Interactive selection (TTY)
6663
- Non-TTY: auto-select; require `--yes`/`--no-input` to apply.
6764

6865
Output
69-
- Writes `agent.modelFallbacks` ordered.
70-
- Writes `agent.imageModelFallbacks` ordered (image-capable models).
71-
- Optional `--set-default` to set `agent.model`.
72-
- Optional `--set-image` to set `agent.imageModel`.
66+
- Writes `agent.model.fallbacks` ordered.
67+
- Writes `agent.imageModel.fallbacks` ordered (image-capable models).
68+
- Ensures `agent.models` entries exist for selected models.
69+
- Optional `--set-default` to set `agent.model.primary`.
70+
- Optional `--set-image` to set `agent.imageModel.primary`.
7371

7472
## Runtime fallback
7573

76-
- On model failure: try `agent.modelFallbacks` in order.
77-
- Ignore fallback entries not in `agent.allowedModels` (if allowlist set).
78-
- Persist last successful provider/model to session entry.
79-
- `/status` shows last used model (not just default).
74+
- On model failure: try `agent.model.fallbacks` in order.
75+
- Per-provider auth failover uses `auth.order` (or stored profile order) **before**
76+
moving to the next model.
77+
- Image routing uses `agent.imageModel` **only when configured** and the primary
78+
model lacks image input.
79+
- Persist last successful provider/model to session entry; auth profile success is global.
8080

8181
## Tests
8282

@@ -86,5 +86,5 @@ Output
8686

8787
## Docs
8888

89-
- Update `docs/configuration.md` with `agent.modelFallbacks`.
89+
- Update `docs/configuration.md` with `agent.models` + `agent.model` + `agent.imageModel`.
9090
- Keep this doc current when CLI surface or scan logic changes.

docs/onboarding.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The macOS app should:
4141
- `~/.clawdbot/credentials/oauth.json` (file mode `0600`, directory mode `0700`)
4242

4343
Why this location matters: it’s the Clawdbot-owned OAuth store.
44-
Clawdbot also imports `oauth.json` into the agent auth store (`~/.clawdbot/agent/auth.json`) on first use.
44+
Clawdbot also imports `oauth.json` into the agent auth profile store (`~/.clawdbot/agent/auth-profiles.json`) on first use.
4545

4646
### Recommended: OAuth (OpenAI Codex)
4747

@@ -148,7 +148,7 @@ If the Gateway runs on another machine, OAuth credentials must be created/stored
148148

149149
For now, remote onboarding should:
150150
- explain why OAuth isn't shown
151-
- point the user at the credential location (`~/.clawdbot/credentials/oauth.json`) and the workspace location on the gateway host
151+
- point the user at the credential location (`~/.clawdbot/credentials/oauth.json`) and the auth profile store (`~/.clawdbot/agent/auth-profiles.json`) on the gateway host
152152
- mention that the **bootstrap ritual happens on the gateway host** (same BOOTSTRAP/IDENTITY/USER files)
153153

154154
### Manual credential setup

docs/proposals/model-config.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Model listing
8787
- alias
8888
- provider
8989
- auth order (from `auth.order`)
90-
- auth source for the current provider (env/auth.json/models.json)
90+
- auth source for the current provider (auth-profiles.json/env/shell env/models.json)
9191

9292
## Fallback behavior (global)
9393

@@ -121,19 +121,20 @@ Support detection
121121
## Migration (doctor + gateway auto-run)
122122

123123
Inputs
124-
- `agent.model` (string)
125-
- `agent.modelFallbacks` (string[])
126-
- `agent.imageModel` (string)
127-
- `agent.imageModelFallbacks` (string[])
128-
- `agent.allowedModels` (string[])
129-
- `agent.modelAliases` (record)
124+
- Legacy keys (pre-migration):
125+
- `agent.model` (string)
126+
- `agent.modelFallbacks` (string[])
127+
- `agent.imageModel` (string)
128+
- `agent.imageModelFallbacks` (string[])
129+
- `agent.allowedModels` (string[])
130+
- `agent.modelAliases` (record)
130131

131132
Outputs
132133
- `agent.models` map with keys for all referenced models
133134
- `agent.model.primary/fallbacks`
134135
- `agent.imageModel.primary/fallbacks`
135-
- `auth.profiles` seeded from current auth.json + env (as `provider:default`)
136-
- `auth.order` seeded with `["provider:default"]`
136+
- Auth profile store seeded from current auth-profiles.json/auth.json + oauth.json + env (as `provider:default`)
137+
- `auth.order` seeded with `["provider:default"]` when config is updated
137138

138139
Auto-run
139140
- Gateway start detects legacy keys and runs doctor migration.

docs/tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Core parameters:
126126
- `maxBytesMb` (optional size cap)
127127

128128
Notes:
129-
- Only available when `agent.imageModel` or `agent.imageModelFallbacks` is set.
129+
- Only available when `agent.imageModel` is configured (primary or fallbacks).
130130
- Uses the image model directly (independent of the main chat model).
131131

132132
### `cron`

0 commit comments

Comments
 (0)