converse-mcp-server 3.7.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +22 -3
- package/README.md +86 -49
- package/docs/API.md +160 -18
- package/docs/EXAMPLES.md +38 -0
- package/docs/PROVIDERS.md +92 -52
- package/package.json +1 -1
- package/src/config.js +36 -6
- package/src/decisionProviders/index.js +174 -0
- package/src/decisionProviders/systemOne.js +199 -0
- package/src/prompts/helpPrompt.js +50 -3
- package/src/providers/anthropic.js +5 -1
- package/src/providers/claude.js +39 -88
- package/src/providers/codex.js +73 -144
- package/src/providers/copilot.js +42 -149
- package/src/providers/deepseek.js +1 -0
- package/src/providers/gemini-cli.js +64 -97
- package/src/providers/google.js +5 -1
- package/src/providers/mistral.js +5 -1
- package/src/providers/openai-compatible.js +4 -1
- package/src/providers/openai.js +5 -1
- package/src/providers/openrouter.js +2 -1
- package/src/providers/xai.js +5 -1
- package/src/services/summarizationService.js +45 -49
- package/src/tools/chat.js +56 -52
- package/src/tools/decide.js +312 -0
- package/src/tools/index.js +2 -0
- package/src/tools/modes/roundtable.js +60 -49
- package/src/utils/localProviderAuth.js +63 -0
- package/src/utils/modelCatalog.js +38 -0
- package/src/utils/modelRouting.js +580 -343
package/docs/PROVIDERS.md
CHANGED
|
@@ -31,7 +31,7 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
31
31
|
- `gemini-2.5-pro` (alias: `pro 2.5`) - Deep reasoning with thinking budget (1M context, 65K output)
|
|
32
32
|
- `gemini-2.5-flash` (alias: `flash`) - Ultra-fast model with thinking budget (1M context, 65K output)
|
|
33
33
|
- `gemini-2.5-flash-lite` (alias: `flash-lite`) - Lightweight fast model (1M context, 65K output)
|
|
34
|
-
- **Note**: The
|
|
34
|
+
- **Note**: The **Antigravity CLI** provider (`agy`, OAuth-based access) serves `gemini-3.8-flash` and `gemini-3.1-pro-preview` under the same IDs and aliases, and it comes before the Google API in bare-name order. When `agy` is installed, bare `pro`, `gemini-pro`, `flash`, `gemini-3.1-pro-preview` and `gemini-3.8-flash` go to Antigravity first; the bare name `gemini` is the Antigravity namespace. Use `google:<model>` (e.g. `google:pro`, `google:gemini-2.5-flash`) to always use the Google API. Names only the Google API serves (e.g. `gemini-2.5-pro`, `gemini-3.5-flash`) route there directly.
|
|
35
35
|
|
|
36
36
|
### X.AI (Grok)
|
|
37
37
|
- **API Key Format**: `xai-...` (starts with `xai-`)
|
|
@@ -41,7 +41,7 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
41
41
|
- `grok-4.5` (default; aliases: `grok`, `grok-4.5-latest`, `grok-build-latest`) - Flagship model with image input, reasoning content, and native web/X search (500K context)
|
|
42
42
|
- **Reasoning**: `reasoning_effort` maps to Grok's `low`/`medium`/`high`. Grok 4.5 always reasons and cannot be turned off, so `none`/`minimal`/`low` clamp to `low`, `medium` stays `medium`, and `high`/`max` clamp to `high`.
|
|
43
43
|
- **Web search**: Automatic — native web/X search (Agent Tools) is attached on every Grok 4.5 request; the model decides per-request whether to search, and any citations are returned in metadata.
|
|
44
|
-
- **Retired IDs**: Older Grok identifiers (e.g. `grok-4-0709`, `grok-code-fast-1`)
|
|
44
|
+
- **Retired IDs**: Older Grok identifiers (e.g. `grok-4-0709`, `grok-code-fast-1`) are not in the catalog and are rejected as unknown models. Use `grok-4.5`.
|
|
45
45
|
|
|
46
46
|
### Anthropic (Claude)
|
|
47
47
|
- **API Key Format**: `sk-ant-...` (starts with `sk-ant-`)
|
|
@@ -100,15 +100,20 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
100
100
|
### Codex
|
|
101
101
|
- **API Key Format**: Optional (uses ChatGPT login by default)
|
|
102
102
|
- **Authentication**: ChatGPT login (system-wide) OR `CODEX_API_KEY`
|
|
103
|
+
- **Availability**: The Codex SDK is installed and either `~/.codex/auth.json` exists (from `codex login`; `$CODEX_HOME/auth.json` when `CODEX_HOME` is set) or `CODEX_API_KEY` is set
|
|
103
104
|
- **Environment Variables**:
|
|
104
105
|
- `CODEX_API_KEY` - Optional API key for headless deployments
|
|
105
106
|
- `CODEX_SANDBOX_MODE` - Filesystem access control (default: read-only)
|
|
106
107
|
- `CODEX_SKIP_GIT_CHECK` - Skip Git repository validation (default: true)
|
|
107
108
|
- `CODEX_APPROVAL_POLICY` - Command approval behavior (default: never)
|
|
108
|
-
- `
|
|
109
|
+
- `CODEX_DEFAULT_MODEL` - Model used for bare `codex` and `auto` (default: `gpt-6-sol`; any Codex model ID or alias below). The legacy name `CODEX_MODEL` is honored when `CODEX_DEFAULT_MODEL` is unset.
|
|
109
110
|
- **Supported Models**:
|
|
110
111
|
- `codex` - OpenAI Codex agentic coding assistant (GPT-6 Sol by default)
|
|
111
|
-
- `codex:<model>` - Same, with an explicit backend
|
|
112
|
+
- `codex:<model>` - Same, with an explicit backend from the Codex catalog:
|
|
113
|
+
- `gpt-6-sol` (aliases: `sol`, `gpt-6`), `gpt-6-luna` (`luna`), `gpt-6-astra` (`astra`)
|
|
114
|
+
- `gpt-5.6-sol` (`gpt-5.6`), `gpt-5.6-terra` (`terra`), `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.3-codex-spark` (`spark`)
|
|
115
|
+
- Names outside this catalog are rejected with suggestions
|
|
116
|
+
- These IDs are also bare names: `gpt-6-astra`, `luna` or `terra` alone go to Codex first and fail over to the OpenAI API (see [Model Routing Logic](#model-routing-logic))
|
|
112
117
|
- `reasoning_effort` is clamped onto what the backend accepts (Sol/Luna: `none`–`max`; GPT-6 Astra: `low`–`max`, no `none`)
|
|
113
118
|
- Thread-based sessions with persistent context
|
|
114
119
|
- Direct filesystem access from working directory
|
|
@@ -134,10 +139,13 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
134
139
|
- Windows (PowerShell): `irm https://antigravity.google/cli/install.ps1 | iex`
|
|
135
140
|
- macOS/Linux: `curl -fsSL https://antigravity.google/cli/install.sh | bash`
|
|
136
141
|
2. Authenticate: run `agy` once interactively and complete the Google OAuth login. This also establishes workspace trust for your home directory (the provider spawns each call in a per-call subdirectory under `~/.converse/agy-runs`).
|
|
137
|
-
- **
|
|
142
|
+
- **Availability**: The `agy` binary is found on PATH or at the platform install location
|
|
143
|
+
- **Environment Variables**:
|
|
144
|
+
- `AGY_DEFAULT_MODEL` - Model used for bare `gemini` and `auto` (default: `gemini-3.8-flash`)
|
|
145
|
+
- **Namespaces**: `gemini`, `agy`, `antigravity`, `gemini-cli` (all equivalent, e.g. `agy:pro`)
|
|
138
146
|
- **Supported Models** (text-only — print mode has no image input channel):
|
|
139
|
-
- `gemini` (
|
|
140
|
-
- `gemini:pro` - Gemini 3.1 Pro
|
|
147
|
+
- `gemini-3.8-flash` (aliases: `flash`, `gemini-3.8`, `flash-3.8`, ...) - Gemini 3.8 Flash (default; `gemini` alone = `gemini:flash`)
|
|
148
|
+
- `gemini-3.1-pro-preview` (aliases: `pro`, `gemini-pro`, `gemini-3.1-pro`, `gemini-3`, ...) - Gemini 3.1 Pro (`gemini:pro`)
|
|
141
149
|
- `reasoning_effort` selects the variant: `low` → (Low), `medium` → (Medium) for Flash / (High) for Pro, `high`/`max` → (High); unset defaults to (High)
|
|
142
150
|
|
|
143
151
|
**Key Features:**
|
|
@@ -184,27 +192,30 @@ agy
|
|
|
184
192
|
|
|
185
193
|
**Best Practices:**
|
|
186
194
|
- Authenticate before first use (run `agy` once interactively to log in)
|
|
187
|
-
- Use
|
|
188
|
-
-
|
|
195
|
+
- Use the `google:` namespace for Google API access (e.g., `google:pro`, `google:gemini-2.5-pro`)
|
|
196
|
+
- The `gemini:` / `agy:` namespaces always use the Antigravity CLI; bare `pro`, `gemini-pro` and `flash` also use it first when `agy` is installed
|
|
189
197
|
- If a call returns an empty response, the CLI is likely not authenticated — run `agy` interactively once
|
|
190
198
|
|
|
191
199
|
**Differences from Google API Provider:**
|
|
192
200
|
- **Authentication**: Google OAuth via `agy` vs API Key (Google API)
|
|
193
201
|
- **Billing**: Antigravity subscription/compute allowance vs pay-per-use API
|
|
194
|
-
- **Model Routing**: `gemini` / `gemini
|
|
202
|
+
- **Model Routing**: `gemini` / `gemini:<model>` / `agy:<model>` → Antigravity CLI provider; `google:<model>` → Google API provider; bare names both serve (`pro`, `gemini-pro`, `gemini-3.1-pro-preview`, `gemini-3.8-flash`) → Antigravity first, then the Google API on authentication/availability failure; bare names only the Google API serves (e.g., `gemini-2.5-pro`) → Google API provider. Bare `flash` is Gemini 3.8 Flash on Antigravity but Gemini 2.5 Flash on the Google API, so it does not fail over between them.
|
|
195
203
|
- **Images**: Not supported (text-only) vs full multimodal on the Google API provider
|
|
204
|
+
- **Permissions**: `agy` runs with `--dangerously-skip-permissions` (headless calls cannot prompt), so every tool request, including shell commands and file writes, is auto-approved. Bare names and `auto` reach it whenever `agy` is installed; use `google:<model>` to keep a request on the plain API.
|
|
196
205
|
|
|
197
206
|
### Claude Agent SDK
|
|
198
207
|
- **Authentication**: Claude Code CLI login (no API key needed)
|
|
199
208
|
- **Setup Required**: Authenticate once with `claude login` (Claude Code CLI)
|
|
200
|
-
- **
|
|
201
|
-
- **
|
|
202
|
-
- `
|
|
203
|
-
|
|
204
|
-
- `claude
|
|
205
|
-
- `claude
|
|
206
|
-
- `claude:
|
|
207
|
-
-
|
|
209
|
+
- **Availability**: The Claude Agent SDK is installed and a credential is present: `~/.claude/.credentials.json` (`$CLAUDE_CONFIG_DIR/.credentials.json` when set), or `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_API_KEY` in the environment. On macOS the login lives in the Keychain and is assumed present. An expired login is caught at call time, and bare-name/`auto` routing fails over to the next provider.
|
|
210
|
+
- **Environment Variables**:
|
|
211
|
+
- `CLAUDE_DEFAULT_MODEL` - Model used for bare `claude` and `auto` (default: `claude-opus-5-5`)
|
|
212
|
+
- **Supported Models** (namespaces: `claude`, `claude-code`, `claude-sdk`):
|
|
213
|
+
- `claude` - Defaults to Claude Opus 5.5 (`claude-opus-5-5`)
|
|
214
|
+
- `claude-opus-5-5` (aliases: `opus`, `claude-opus`, `opus-5.5`) - Claude Opus 5.5 (`claude:opus`)
|
|
215
|
+
- `claude-opus-5` (alias: `opus-5`) - Claude Opus 5
|
|
216
|
+
- `claude-fable-5-1` (aliases: `fable`, `claude-fable`, `fable-5.1`) - Claude Fable 5.1 (`claude:fable`)
|
|
217
|
+
- `claude-fable-5` (alias: `fable-5`) - Claude Fable 5.0
|
|
218
|
+
- Names outside this catalog are rejected with suggestions (e.g. `claude:sonnet` suggests `copilot:sonnet` and `anthropic:sonnet`)
|
|
208
219
|
|
|
209
220
|
**Key Features:**
|
|
210
221
|
- **Subscription Access**: Uses your Claude subscription instead of API credits
|
|
@@ -217,19 +228,22 @@ agy
|
|
|
217
228
|
**Differences from Anthropic API Provider:**
|
|
218
229
|
- **Authentication**: Claude Code login vs `ANTHROPIC_API_KEY`
|
|
219
230
|
- **Billing**: Claude subscription vs pay-per-use API
|
|
220
|
-
- **Model Routing**: `claude` and `claude
|
|
231
|
+
- **Model Routing**: `claude` and `claude:<model>` → SDK provider; `anthropic:<model>` → API provider; bare names both serve as the same model (`opus`, `claude-opus-5-5`, `claude-opus-5`, `claude-fable-5`) → SDK first, then the API on authentication/availability failure; bare names only the API serves (e.g., `sonnet`, `haiku`) → API provider. Bare `fable` is Fable 5.1 on the SDK but Fable 5 on the API, so it does not fail over between them.
|
|
232
|
+
- **Permissions**: The SDK runs with `bypassPermissions`, and bare names and `auto` reach it whenever it is available. Use `anthropic:<model>` to keep a request on the plain API.
|
|
221
233
|
|
|
222
234
|
### GitHub Copilot SDK
|
|
223
235
|
- **Authentication**: GitHub Copilot subscription via the Copilot CLI (`gh auth login` with an active Copilot subscription) — no API key needed
|
|
224
236
|
- **Setup Required**: Authenticate the GitHub CLI and ensure your account has an active Copilot subscription
|
|
225
|
-
- **
|
|
226
|
-
- **
|
|
227
|
-
- `
|
|
228
|
-
|
|
237
|
+
- **Availability**: The Copilot SDK (`@github/copilot-sdk`) is installed
|
|
238
|
+
- **Environment Variables**:
|
|
239
|
+
- `COPILOT_DEFAULT_MODEL` - Model used for bare `copilot` and `auto` (default: `gpt-6-sol`). The legacy name `COPILOT_MODEL` is honored when `COPILOT_DEFAULT_MODEL` is unset.
|
|
240
|
+
- **Supported Models** (namespace-only: reach them with `copilot:`, `github-copilot:` or `copilot-sdk:`, e.g. `copilot:gpt-6-sol`; Copilot never serves bare model names):
|
|
241
|
+
- `copilot` - GPT-6 Sol, or `COPILOT_DEFAULT_MODEL`
|
|
242
|
+
- OpenAI: `gpt-6-sol` (aliases: `gpt-6`, `gpt-5`, `sol`), `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna`
|
|
229
243
|
- Anthropic: `claude-opus-5.5` (aliases: `opus`, `claude`; Copilot Pro+/Max/Business/Enterprise), `claude-fable-5` (alias: `fable`), `claude-sonnet-5` (alias: `sonnet`), `claude-opus-5`, `claude-opus-4.8`
|
|
230
244
|
- Google: `gemini-3.1-pro-preview` (aliases: `gemini`, `gemini-3.1-pro`), `gemini-3.8-flash` (aliases: `gemini-3.8`, `flash-3.8`), `gemini-3.5-flash` (alias: `gemini-flash`)
|
|
231
245
|
- **Reasoning**: The GPT-6 and GPT-5.6 tiers accept `reasoning_effort` (clamped onto Copilot's `low`–`xhigh`).
|
|
232
|
-
- **
|
|
246
|
+
- **Unknown IDs**: Any other `copilot:<id>` is rejected with suggestions; only the IDs and aliases above are accepted.
|
|
233
247
|
|
|
234
248
|
**Key Features:**
|
|
235
249
|
- **Subscription Access**: Uses your GitHub Copilot subscription instead of API credits
|
|
@@ -259,6 +273,24 @@ CODEX_SKIP_GIT_CHECK=true # true (default), false
|
|
|
259
273
|
CODEX_APPROVAL_POLICY=never # never (default), untrusted, on-failure, on-request
|
|
260
274
|
```
|
|
261
275
|
|
|
276
|
+
### Default Model Overrides (.env file)
|
|
277
|
+
|
|
278
|
+
Each provider has a `<PROVIDER>_DEFAULT_MODEL` variable that sets the model used for its bare provider name (`codex`, `openai`, ...) and for `auto`. The value must be a model ID or alias from that provider's list; startup fails with "Did you mean" suggestions otherwise. OpenRouter also accepts any `vendor/model` slug.
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
CODEX_DEFAULT_MODEL=gpt-6-sol # CODEX_MODEL is honored as a legacy fallback
|
|
282
|
+
CLAUDE_DEFAULT_MODEL=claude-opus-5-5
|
|
283
|
+
AGY_DEFAULT_MODEL=gemini-3.8-flash # Antigravity CLI (gemini)
|
|
284
|
+
COPILOT_DEFAULT_MODEL=gpt-6-sol # COPILOT_MODEL is honored as a legacy fallback
|
|
285
|
+
OPENAI_DEFAULT_MODEL=gpt-6-sol
|
|
286
|
+
GOOGLE_DEFAULT_MODEL=gemini-3.1-pro-preview
|
|
287
|
+
XAI_DEFAULT_MODEL=grok-4.5
|
|
288
|
+
ANTHROPIC_DEFAULT_MODEL=claude-opus-5-5
|
|
289
|
+
MISTRAL_DEFAULT_MODEL=mistral-medium-3-5
|
|
290
|
+
DEEPSEEK_DEFAULT_MODEL=deepseek-v4-pro
|
|
291
|
+
OPENROUTER_DEFAULT_MODEL=z-ai/glm-5.2 # any vendor/model slug is accepted
|
|
292
|
+
```
|
|
293
|
+
|
|
262
294
|
### Claude Configuration (claude_desktop_config.json)
|
|
263
295
|
```json
|
|
264
296
|
{
|
|
@@ -311,6 +343,7 @@ All providers support streaming responses for real-time output.
|
|
|
311
343
|
|
|
312
344
|
### Local Execution
|
|
313
345
|
- **Codex**: Runs locally with direct filesystem access and thread-based sessions
|
|
346
|
+
- **Claude Agent SDK, Antigravity CLI, Copilot SDK**: Run through a local SDK or CLI using your subscription login
|
|
314
347
|
- **All Others**: API-based remote execution
|
|
315
348
|
|
|
316
349
|
## Model Selection in Tools
|
|
@@ -319,42 +352,47 @@ When using the chat tool in any mode, specify models using their identifiers:
|
|
|
319
352
|
|
|
320
353
|
### Model Routing Logic
|
|
321
354
|
|
|
322
|
-
|
|
355
|
+
Routing is derived entirely from each provider's model list (canonical IDs plus aliases, matched case-insensitively). Every entry in `models` takes one of four forms:
|
|
356
|
+
|
|
357
|
+
1. **`provider`** — that provider's default model (hardcoded, or `<PROVIDER>_DEFAULT_MODEL`). Namespace tokens:
|
|
323
358
|
- `codex` → Codex
|
|
324
|
-
- `gemini`, `
|
|
325
|
-
- `claude`, `claude-
|
|
326
|
-
- `copilot`, `
|
|
359
|
+
- `gemini`, `agy`, `antigravity`, `gemini-cli` → Gemini via Antigravity CLI
|
|
360
|
+
- `claude`, `claude-code`, `claude-sdk` → Claude Agent SDK
|
|
361
|
+
- `copilot`, `github-copilot`, `copilot-sdk` → Copilot SDK
|
|
362
|
+
- `openai`, `google`, `xai`, `anthropic`, `mistral`, `deepseek`, `openrouter` → the matching API provider
|
|
363
|
+
|
|
364
|
+
2. **`provider:model`** — that model on that provider only (e.g. `codex:astra`, `gemini:pro`, `google:pro`, `anthropic:opus`, `copilot:sonnet`). The model must be in that provider's list; there is no failover to another provider.
|
|
365
|
+
|
|
366
|
+
3. **Bare `model`** — an ID or alias without a namespace goes to the first provider, in this order, whose list contains the name and that is set up: Codex, Antigravity CLI, Claude Agent SDK, OpenAI, Google, X.AI, Anthropic, Mistral, DeepSeek, OpenRouter.
|
|
367
|
+
- "Set up" means an API key for API providers; for Codex, the SDK plus a login file or `CODEX_API_KEY`; for the Claude Agent SDK, the SDK plus a login file, a macOS login, or `CLAUDE_CODE_OAUTH_TOKEN`/`ANTHROPIC_API_KEY`; for Antigravity, the `agy` binary.
|
|
368
|
+
- If that provider fails with an authentication or availability error (including an expired login, which is only detected at call time), the next set-up provider that serves **the same model** takes over. A provider whose alias of that name points at a different model is never substituted (bare `fable` is Fable 5.1 on the Claude Agent SDK and Fable 5 on the Anthropic API; bare `flash` is Gemini 3.8 Flash on Antigravity and Gemini 2.5 Flash on the Google API).
|
|
369
|
+
- Copilot never serves bare names; use `copilot:<model>`.
|
|
370
|
+
|
|
371
|
+
4. **`auto`** — the first available provider's default model, in the priority order above with Copilot between the Claude Agent SDK and OpenAI.
|
|
327
372
|
|
|
328
|
-
|
|
329
|
-
- Contains "gpt", "o1", "o3", "o4" → OpenAI
|
|
330
|
-
- Contains "claude", "fable", "opus", "sonnet", "haiku" → Anthropic
|
|
331
|
-
- Contains "gemini", "flash", "pro" → Google
|
|
332
|
-
- Contains "grok" → X.AI
|
|
333
|
-
- Contains "mistral", "magistral" → Mistral
|
|
334
|
-
- Contains "deepseek", "reasoner", "r1" → DeepSeek
|
|
335
|
-
- Contains "qwen", "kimi", "k2" → OpenRouter
|
|
373
|
+
**Unknown names are rejected**, never guessed or forwarded: a typo or an unlisted model returns an error with up to three close matches, e.g. `Unknown model "gtp-6-astra". Did you mean: gpt-6-astra?` or `Unknown openai model "spark" in "openai:spark". Did you mean: codex:spark?`.
|
|
336
374
|
|
|
337
|
-
|
|
338
|
-
- If exact model exists in a native provider → Routes to that provider
|
|
339
|
-
- If not found in any native provider → Routes to OpenRouter
|
|
340
|
-
- This allows using models like "anthropic/claude-3.5-sonnet" via OpenRouter
|
|
375
|
+
**OpenRouter slugs** are the one open-ended case: a full `vendor/model` slug (bare, or with the `openrouter:` namespace) routes to OpenRouter and is validated against OpenRouter's live catalog. `openrouter/auto` (aliases `auto-router`, `openrouter-auto`) selects OpenRouter's auto-router.
|
|
341
376
|
|
|
342
|
-
|
|
343
|
-
- "openrouter/auto", "openrouter auto", "auto router", "auto-router"
|
|
377
|
+
**Local agent permissions**: Bare names and `auto` reach the local agent providers whenever they are set up. The Antigravity CLI auto-approves every tool request (`--dangerously-skip-permissions`) and the Claude Agent SDK runs with `bypassPermissions`. Name the API provider (`google:pro`, `anthropic:opus`, `openai:gpt-6-astra`) to keep a request off the local agents.
|
|
344
378
|
|
|
345
|
-
|
|
379
|
+
Examples:
|
|
346
380
|
|
|
347
381
|
```text
|
|
348
|
-
"gpt-6" //
|
|
349
|
-
"
|
|
350
|
-
"
|
|
351
|
-
"
|
|
382
|
+
"gpt-6" // Codex (gpt-6-sol), else OpenAI API
|
|
383
|
+
"openai:gpt-6" // OpenAI API only
|
|
384
|
+
"fable" // Claude Agent SDK (claude-fable-5-1) when set up, otherwise Anthropic API (claude-fable-5)
|
|
385
|
+
"opus" // Claude Agent SDK (claude-opus-5-5), else Anthropic API
|
|
386
|
+
"anthropic:opus" // Anthropic API only
|
|
387
|
+
"sonnet" // Anthropic API (claude-sonnet-4-6)
|
|
352
388
|
"claude" // Claude Agent SDK (defaults to Claude Opus 5.5)
|
|
353
389
|
"claude:fable" // Claude Agent SDK (Claude Fable 5.1)
|
|
354
|
-
"gemini-
|
|
355
|
-
"
|
|
390
|
+
"pro" // Antigravity CLI (gemini-3.1-pro-preview), else Google API
|
|
391
|
+
"google:pro" // Google API only
|
|
392
|
+
"gemini-2.5-pro" // Google API
|
|
393
|
+
"grok-4.5" // X.AI
|
|
356
394
|
"mistral-large" // Mistral (alias -> mistral-large-2512)
|
|
357
|
-
"deepseek" // DeepSeek
|
|
395
|
+
"deepseek" // DeepSeek default model (deepseek-v4-pro)
|
|
358
396
|
"z-ai/glm-5.2" // OpenRouter (curated slug)
|
|
359
397
|
"z-ai/glm-5.2:online" // OpenRouter with web search opt-in
|
|
360
398
|
"anthropic/claude-sonnet-5" // OpenRouter (any full slug routes as-is)
|
|
@@ -381,9 +419,11 @@ The `models` array always holds plain model-name strings. Each string routes as
|
|
|
381
419
|
- Verify API keys are active and have available quota
|
|
382
420
|
|
|
383
421
|
### Model Not Found
|
|
384
|
-
- Use
|
|
385
|
-
-
|
|
422
|
+
- Use model identifiers or aliases as listed above; anything else is rejected with up to three "Did you mean" suggestions
|
|
423
|
+
- Check the namespace: a model must be in that provider's list (`openai:spark` fails and suggests `codex:spark`)
|
|
424
|
+
- Aliases can differ per provider (e.g., bare "fable" → "claude-fable-5-1" on the Claude Agent SDK, `anthropic:fable` → "claude-fable-5")
|
|
386
425
|
- Note: bare "claude" routes to the Claude Agent SDK provider, not the Anthropic API
|
|
426
|
+
- A "none is available" error means the model exists but no provider serving it is set up; the message names the setup step for each
|
|
387
427
|
- Check provider documentation for model availability in your region
|
|
388
428
|
|
|
389
429
|
### Rate Limits
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -13,6 +13,10 @@ import { fileURLToPath } from 'url';
|
|
|
13
13
|
import { dirname, join, resolve } from 'path';
|
|
14
14
|
import { readFileSync } from 'fs';
|
|
15
15
|
import { findAgyBinary } from './providers/gemini-cli.js';
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_MODEL_ENV_VARS,
|
|
18
|
+
validateDefaultModelOverrides,
|
|
19
|
+
} from './utils/modelRouting.js';
|
|
16
20
|
|
|
17
21
|
// Load environment variables from appropriate .env file
|
|
18
22
|
// Priority: .env.test (for test env) > .env (default)
|
|
@@ -210,6 +214,12 @@ const CONFIG_SCHEMA = {
|
|
|
210
214
|
secret: true,
|
|
211
215
|
description: 'OpenRouter API key',
|
|
212
216
|
},
|
|
217
|
+
TYPESAFE_API_KEY: {
|
|
218
|
+
type: 'string',
|
|
219
|
+
required: false,
|
|
220
|
+
secret: true,
|
|
221
|
+
description: 'TypeSafe API key (System One decision models for the decide tool)',
|
|
222
|
+
},
|
|
213
223
|
},
|
|
214
224
|
|
|
215
225
|
// Provider-specific configuration
|
|
@@ -280,9 +290,8 @@ const CONFIG_SCHEMA = {
|
|
|
280
290
|
},
|
|
281
291
|
CODEX_MODEL: {
|
|
282
292
|
type: 'string',
|
|
283
|
-
|
|
284
|
-
description:
|
|
285
|
-
'Default Codex backend model (e.g., gpt-6-sol, gpt-6-luna, gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5)',
|
|
293
|
+
required: false,
|
|
294
|
+
description: 'Deprecated alias of CODEX_DEFAULT_MODEL',
|
|
286
295
|
},
|
|
287
296
|
|
|
288
297
|
// Copilot configuration
|
|
@@ -294,8 +303,7 @@ const CONFIG_SCHEMA = {
|
|
|
294
303
|
COPILOT_MODEL: {
|
|
295
304
|
type: 'string',
|
|
296
305
|
required: false,
|
|
297
|
-
description:
|
|
298
|
-
'Default model for Copilot SDK sessions (e.g., gpt-6-sol, claude-opus-5.5, claude-sonnet-5)',
|
|
306
|
+
description: 'Deprecated alias of COPILOT_DEFAULT_MODEL',
|
|
299
307
|
},
|
|
300
308
|
COPILOT_CLI_PATH: {
|
|
301
309
|
type: 'string',
|
|
@@ -303,6 +311,20 @@ const CONFIG_SCHEMA = {
|
|
|
303
311
|
description:
|
|
304
312
|
'Explicit path to the Copilot CLI runtime (index.js or copilot binary). Overrides automatic resolution.',
|
|
305
313
|
},
|
|
314
|
+
|
|
315
|
+
// Per-provider default models: the model a bare provider name (`codex`,
|
|
316
|
+
// `openai`, ...) or "auto" uses. Each must name a model or alias in that
|
|
317
|
+
// provider's catalog; startup fails with suggestions otherwise.
|
|
318
|
+
...Object.fromEntries(
|
|
319
|
+
Object.entries(DEFAULT_MODEL_ENV_VARS).map(([provider, envVar]) => [
|
|
320
|
+
envVar,
|
|
321
|
+
{
|
|
322
|
+
type: 'string',
|
|
323
|
+
required: false,
|
|
324
|
+
description: `Default model for the ${provider} provider`,
|
|
325
|
+
},
|
|
326
|
+
]),
|
|
327
|
+
),
|
|
306
328
|
},
|
|
307
329
|
|
|
308
330
|
// MCP configuration
|
|
@@ -691,7 +713,7 @@ export async function loadConfig() {
|
|
|
691
713
|
|
|
692
714
|
if (availableKeys.length === 0 && !hasVertexAI && !hasSdkProvider) {
|
|
693
715
|
errors.push(
|
|
694
|
-
'At least one API key must be configured: OPENAI_API_KEY, XAI_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY, or
|
|
716
|
+
'At least one API key must be configured: OPENAI_API_KEY, XAI_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY, OPENROUTER_API_KEY, or TYPESAFE_API_KEY. Alternatively, configure Google Vertex AI or use an SDK-based provider (codex, claude, copilot) or the Antigravity CLI (gemini-cli).',
|
|
695
717
|
);
|
|
696
718
|
}
|
|
697
719
|
|
|
@@ -892,6 +914,14 @@ export async function validateRuntimeConfig(config) {
|
|
|
892
914
|
// Validate Codex configuration
|
|
893
915
|
validateCodexConfig(config);
|
|
894
916
|
|
|
917
|
+
// Validate per-provider default-model overrides against provider catalogs.
|
|
918
|
+
// Imported lazily: the provider registry pulls in every provider SDK.
|
|
919
|
+
const { getProviders } = await import('./providers/index.js');
|
|
920
|
+
const defaultModelErrors = validateDefaultModelOverrides(getProviders(), config);
|
|
921
|
+
if (defaultModelErrors.length > 0) {
|
|
922
|
+
throw new ConfigurationError(defaultModelErrors.join('\n'));
|
|
923
|
+
}
|
|
924
|
+
|
|
895
925
|
// Validate environment
|
|
896
926
|
const validEnvs = ['development', 'production', 'test'];
|
|
897
927
|
if (!validEnvs.includes(config.environment.nodeEnv)) {
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision Providers
|
|
3
|
+
*
|
|
4
|
+
* Hosts of System One decision models (TypeSafe's Jev family). These are kept
|
|
5
|
+
* apart from the chat provider registry on purpose: a decision model takes a
|
|
6
|
+
* state plus typed questions and returns probabilities, never text, so it must
|
|
7
|
+
* never be reachable from `chat` routing ("auto", bare names, failover), and
|
|
8
|
+
* the `decide` tool must never reach a chat model.
|
|
9
|
+
*
|
|
10
|
+
* Spec grammar mirrors chat routing:
|
|
11
|
+
* - `auto` (or empty) — every configured provider's default, in priority order
|
|
12
|
+
* - `provider` / `provider:` — that provider's default model
|
|
13
|
+
* - `provider:model` — that provider only
|
|
14
|
+
* - `model` — every provider that serves the name, in priority order; the
|
|
15
|
+
* first is used and the rest are failover candidates
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { getCustomHeaders as getOpenRouterAttributionHeaders } from '../providers/openrouter.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Model catalogs map each provider's own model ID to the names that route to
|
|
22
|
+
* it. OpenRouter only knows `~typesafe/jev-latest` and `typesafe/jev-1.13`
|
|
23
|
+
* (it rejects `jev-1.13.0` and `jev-preview`), so native IDs are translated
|
|
24
|
+
* per provider rather than by a blanket prefix rewrite.
|
|
25
|
+
*/
|
|
26
|
+
export const DECISION_PROVIDERS = {
|
|
27
|
+
typesafe: {
|
|
28
|
+
name: 'typesafe',
|
|
29
|
+
label: 'TypeSafe',
|
|
30
|
+
baseURL: 'https://api.typesafe.ai',
|
|
31
|
+
apiKeyEnv: 'TYPESAFE_API_KEY',
|
|
32
|
+
defaultModel: 'jev-latest',
|
|
33
|
+
models: {
|
|
34
|
+
'jev-latest': ['jev', '~typesafe/jev-latest'],
|
|
35
|
+
'jev-1.13.0': ['jev-1.13', 'typesafe/jev-1.13'],
|
|
36
|
+
'jev-preview': [],
|
|
37
|
+
},
|
|
38
|
+
// TypeSafe accepts every published versioned ID, listed or not.
|
|
39
|
+
passthrough: (name) => /^jev-\d+\.\d+(\.\d+)?$/i.test(name),
|
|
40
|
+
headers: (config) => ({ Authorization: `Bearer ${config.apiKeys.typesafe}` }),
|
|
41
|
+
isAvailable: (config) => Boolean(config?.apiKeys?.typesafe),
|
|
42
|
+
},
|
|
43
|
+
openrouter: {
|
|
44
|
+
name: 'openrouter',
|
|
45
|
+
label: 'OpenRouter',
|
|
46
|
+
baseURL: 'https://openrouter.ai/api',
|
|
47
|
+
apiKeyEnv: 'OPENROUTER_API_KEY',
|
|
48
|
+
defaultModel: '~typesafe/jev-latest',
|
|
49
|
+
models: {
|
|
50
|
+
'~typesafe/jev-latest': ['jev-latest', 'jev'],
|
|
51
|
+
'typesafe/jev-1.13': ['jev-1.13', 'jev-1.13.0'],
|
|
52
|
+
},
|
|
53
|
+
// Decision models OpenRouter adds later are reachable by full slug.
|
|
54
|
+
passthrough: (name) => name.includes('/'),
|
|
55
|
+
headers: (config) => ({
|
|
56
|
+
Authorization: `Bearer ${config.apiKeys.openrouter}`,
|
|
57
|
+
...getOpenRouterAttributionHeaders(config),
|
|
58
|
+
}),
|
|
59
|
+
isAvailable: (config) => Boolean(config?.apiKeys?.openrouter),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Failover order: the native host first, then OpenRouter. */
|
|
64
|
+
export const DECISION_PROVIDER_PRIORITY = ['typesafe', 'openrouter'];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve a name against one provider's catalog, then its passthrough rule.
|
|
68
|
+
* @returns {string|null} The model ID to send to that provider
|
|
69
|
+
*/
|
|
70
|
+
export function findDecisionModel(provider, name) {
|
|
71
|
+
const wanted = String(name).trim().toLowerCase();
|
|
72
|
+
if (!wanted) return null;
|
|
73
|
+
for (const [id, aliases] of Object.entries(provider.models)) {
|
|
74
|
+
if (id.toLowerCase() === wanted || aliases.some((a) => a.toLowerCase() === wanted)) {
|
|
75
|
+
return id;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return provider.passthrough(String(name).trim()) ? String(name).trim() : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function setupHint(provider) {
|
|
82
|
+
return `set ${provider.apiKeyEnv}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function knownModels() {
|
|
86
|
+
const names = new Set();
|
|
87
|
+
for (const providerName of DECISION_PROVIDER_PRIORITY) {
|
|
88
|
+
const provider = DECISION_PROVIDERS[providerName];
|
|
89
|
+
for (const [id, aliases] of Object.entries(provider.models)) {
|
|
90
|
+
names.add(id);
|
|
91
|
+
aliases.forEach((a) => names.add(a));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return [...names];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function ok(candidates) {
|
|
98
|
+
return { status: 'ok', candidates, error: null };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function fail(status, error) {
|
|
102
|
+
return { status, candidates: [], error };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resolve a decision model spec into an ordered candidate list.
|
|
107
|
+
* @param {string} spec - Model spec (see module grammar)
|
|
108
|
+
* @param {object} config - Server configuration (for API keys)
|
|
109
|
+
* @returns {{ status: 'ok'|'unknown'|'unavailable', candidates: Array<{ providerName: string, provider: object, model: string }>, error: string|null }}
|
|
110
|
+
*/
|
|
111
|
+
export function resolveDecisionModel(spec, config) {
|
|
112
|
+
const raw = String(spec ?? '').trim();
|
|
113
|
+
const namespaces = DECISION_PROVIDER_PRIORITY.join(', ');
|
|
114
|
+
|
|
115
|
+
if (!raw || raw.toLowerCase() === 'auto') {
|
|
116
|
+
const candidates = DECISION_PROVIDER_PRIORITY
|
|
117
|
+
.map((name) => DECISION_PROVIDERS[name])
|
|
118
|
+
.filter((provider) => provider.isAvailable(config))
|
|
119
|
+
.map((provider) => ({ providerName: provider.name, provider, model: provider.defaultModel }));
|
|
120
|
+
if (candidates.length === 0) {
|
|
121
|
+
return fail(
|
|
122
|
+
'unavailable',
|
|
123
|
+
`No decision provider is configured: ${DECISION_PROVIDER_PRIORITY.map((n) => `${n} (${setupHint(DECISION_PROVIDERS[n])})`).join('; ')}.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return ok(candidates);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const colon = raw.indexOf(':');
|
|
130
|
+
const hasNamespace = colon > 0 && !raw.slice(0, colon).includes('/');
|
|
131
|
+
const namespace = hasNamespace ? raw.slice(0, colon).toLowerCase() : raw.toLowerCase();
|
|
132
|
+
|
|
133
|
+
if (hasNamespace || DECISION_PROVIDERS[namespace]) {
|
|
134
|
+
const provider = DECISION_PROVIDERS[namespace];
|
|
135
|
+
if (!provider) {
|
|
136
|
+
return fail('unknown', `Unknown decision provider "${raw.slice(0, colon)}". Providers: ${namespaces}.`);
|
|
137
|
+
}
|
|
138
|
+
const name = hasNamespace ? raw.slice(colon + 1).trim() : '';
|
|
139
|
+
const model = name ? findDecisionModel(provider, name) : provider.defaultModel;
|
|
140
|
+
if (!model) {
|
|
141
|
+
return fail(
|
|
142
|
+
'unknown',
|
|
143
|
+
`${provider.label} does not serve "${name}". Models: ${Object.keys(provider.models).join(', ')}.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if (!provider.isAvailable(config)) {
|
|
147
|
+
return fail('unavailable', `${provider.label} is not configured (${setupHint(provider)}).`);
|
|
148
|
+
}
|
|
149
|
+
return ok([{ providerName: provider.name, provider, model }]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const matches = DECISION_PROVIDER_PRIORITY
|
|
153
|
+
.map((providerName) => {
|
|
154
|
+
const provider = DECISION_PROVIDERS[providerName];
|
|
155
|
+
const model = findDecisionModel(provider, raw);
|
|
156
|
+
return model ? { providerName, provider, model } : null;
|
|
157
|
+
})
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
|
|
160
|
+
if (matches.length === 0) {
|
|
161
|
+
return fail(
|
|
162
|
+
'unknown',
|
|
163
|
+
`Unknown decision model "${raw}". Use "auto", a provider (${namespaces}), "provider:model", or one of: ${knownModels().join(', ')}.`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const available = matches.filter((m) => m.provider.isAvailable(config));
|
|
167
|
+
if (available.length === 0) {
|
|
168
|
+
return fail(
|
|
169
|
+
'unavailable',
|
|
170
|
+
`"${raw}" is served by ${matches.map((m) => `${m.providerName} (${setupHint(m.provider)})`).join('; ')}, but none is configured.`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return ok(available);
|
|
174
|
+
}
|