converse-mcp-server 3.7.1 → 4.0.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 +17 -3
- package/README.md +67 -49
- package/docs/API.md +55 -18
- package/docs/EXAMPLES.md +38 -0
- package/docs/PROVIDERS.md +92 -52
- package/package.json +1 -1
- package/src/config.js +29 -5
- package/src/prompts/helpPrompt.js +27 -2
- 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 +1 -0
- package/src/providers/xai.js +5 -1
- package/src/services/summarizationService.js +45 -49
- package/src/tools/chat.js +56 -52
- 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/src/providers/claude.js
CHANGED
|
@@ -16,14 +16,20 @@
|
|
|
16
16
|
import { debugLog, debugError } from '../utils/console.js';
|
|
17
17
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
18
18
|
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
19
|
+
import { findCatalogEntry, findCatalogId } from '../utils/modelCatalog.js';
|
|
20
|
+
import {
|
|
21
|
+
hasClaudeCredentials,
|
|
22
|
+
isPackageResolvable,
|
|
23
|
+
} from '../utils/localProviderAuth.js';
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
const DEFAULT_SDK_MODEL = 'claude-opus-5-5';
|
|
25
|
+
const DEFAULT_MODEL = 'claude-opus-5-5';
|
|
22
26
|
const SDK_EFFORT_TIERS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
27
|
+
const CLAUDE_SDK_PACKAGE = '@anthropic-ai/claude-agent-sdk';
|
|
23
28
|
|
|
24
|
-
//
|
|
29
|
+
// Keyed by the model ID the Agent SDK accepts, which is also the canonical ID
|
|
30
|
+
// the router resolves to.
|
|
25
31
|
const SUPPORTED_MODELS = {
|
|
26
|
-
opus: {
|
|
32
|
+
'claude-opus-5-5': {
|
|
27
33
|
modelName: 'claude-opus-5-5',
|
|
28
34
|
friendlyName: 'Claude Opus 5.5 (via Agent SDK)',
|
|
29
35
|
contextWindow: 1000000,
|
|
@@ -35,18 +41,14 @@ const SUPPORTED_MODELS = {
|
|
|
35
41
|
description:
|
|
36
42
|
'Claude Opus 5.5 via Agent SDK (default) - requires claude login authentication',
|
|
37
43
|
aliases: [
|
|
38
|
-
'
|
|
39
|
-
'claude-sdk',
|
|
40
|
-
'claude-code',
|
|
41
|
-
'claude:opus',
|
|
44
|
+
'opus',
|
|
42
45
|
'claude-opus',
|
|
43
|
-
'claude-opus-5-5',
|
|
44
46
|
'claude-opus-5.5',
|
|
45
47
|
'opus-5-5',
|
|
46
48
|
'opus-5.5',
|
|
47
49
|
],
|
|
48
50
|
},
|
|
49
|
-
'opus-5': {
|
|
51
|
+
'claude-opus-5': {
|
|
50
52
|
modelName: 'claude-opus-5',
|
|
51
53
|
friendlyName: 'Claude Opus 5 (via Agent SDK)',
|
|
52
54
|
contextWindow: 1000000,
|
|
@@ -57,9 +59,9 @@ const SUPPORTED_MODELS = {
|
|
|
57
59
|
timeout: 1800000,
|
|
58
60
|
description:
|
|
59
61
|
'Claude Opus 5 via Agent SDK - requires claude login authentication',
|
|
60
|
-
aliases: ['
|
|
62
|
+
aliases: ['opus-5', 'opus5'],
|
|
61
63
|
},
|
|
62
|
-
fable: {
|
|
64
|
+
'claude-fable-5-1': {
|
|
63
65
|
modelName: 'claude-fable-5-1',
|
|
64
66
|
friendlyName: 'Claude Fable 5.1 (via Agent SDK)',
|
|
65
67
|
contextWindow: 1000000,
|
|
@@ -71,15 +73,14 @@ const SUPPORTED_MODELS = {
|
|
|
71
73
|
description:
|
|
72
74
|
'Claude Fable 5.1 via Agent SDK - requires claude login authentication',
|
|
73
75
|
aliases: [
|
|
74
|
-
'
|
|
76
|
+
'fable',
|
|
75
77
|
'claude-fable',
|
|
76
|
-
'claude-fable-5-1',
|
|
77
78
|
'claude-fable-5.1',
|
|
78
79
|
'fable-5-1',
|
|
79
80
|
'fable-5.1',
|
|
80
81
|
],
|
|
81
82
|
},
|
|
82
|
-
'fable-5': {
|
|
83
|
+
'claude-fable-5': {
|
|
83
84
|
modelName: 'claude-fable-5',
|
|
84
85
|
friendlyName: 'Claude Fable 5 (via Agent SDK)',
|
|
85
86
|
contextWindow: 1000000,
|
|
@@ -90,7 +91,7 @@ const SUPPORTED_MODELS = {
|
|
|
90
91
|
timeout: 1800000,
|
|
91
92
|
description:
|
|
92
93
|
'Claude Fable 5 via Agent SDK - requires claude login authentication',
|
|
93
|
-
aliases: ['
|
|
94
|
+
aliases: ['fable-5', 'fable5'],
|
|
94
95
|
},
|
|
95
96
|
};
|
|
96
97
|
|
|
@@ -104,25 +105,12 @@ class ClaudeProviderError extends ProviderError {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
/**
|
|
108
|
-
* Check if Claude SDK is available (optional dependency)
|
|
109
|
-
*/
|
|
110
|
-
function isClaudeSDKAvailable() {
|
|
111
|
-
try {
|
|
112
|
-
// Simple presence check that works in ES modules
|
|
113
|
-
// If SDK not available, the actual import() will fail later with clear error
|
|
114
|
-
return true;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
108
|
/**
|
|
121
109
|
* Dynamically import Claude SDK (lazy loading)
|
|
122
110
|
* This keeps the SDK as an optional dependency
|
|
123
111
|
*/
|
|
124
112
|
async function getClaudeSDK() {
|
|
125
|
-
if (!
|
|
113
|
+
if (!isPackageResolvable(CLAUDE_SDK_PACKAGE)) {
|
|
126
114
|
throw new ClaudeProviderError(
|
|
127
115
|
'Claude SDK not installed. Install with: npm install @anthropic-ai/claude-agent-sdk',
|
|
128
116
|
'CLAUDE_SDK_NOT_INSTALLED',
|
|
@@ -143,58 +131,24 @@ async function getClaudeSDK() {
|
|
|
143
131
|
}
|
|
144
132
|
|
|
145
133
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
let name = modelName.trim();
|
|
153
|
-
if (name.toLowerCase().startsWith('claude:')) {
|
|
154
|
-
name = name.slice('claude:'.length).trim();
|
|
155
|
-
}
|
|
156
|
-
if (!name) return SUPPORTED_MODELS.opus;
|
|
157
|
-
|
|
158
|
-
const nameLower = name.toLowerCase();
|
|
159
|
-
|
|
160
|
-
if (SUPPORTED_MODELS[nameLower]) {
|
|
161
|
-
return SUPPORTED_MODELS[nameLower];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (const config of Object.values(SUPPORTED_MODELS)) {
|
|
165
|
-
if (
|
|
166
|
-
config.aliases &&
|
|
167
|
-
config.aliases.some((alias) => alias.toLowerCase() === nameLower)
|
|
168
|
-
) {
|
|
169
|
-
return config;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Resolve the requested model to the underlying SDK model ID.
|
|
178
|
-
* - "claude" (and bare "claude:") defaults to Claude Opus 5.5
|
|
179
|
-
* - "claude:opus" / "claude:opus-5" / "claude:fable" / "claude:fable-5" select the specific model
|
|
180
|
-
* - Unknown names are passed through (after prefix stripping) so users can
|
|
181
|
-
* target any model ID the Agent SDK accepts (e.g. "claude:claude-sonnet-4-6")
|
|
134
|
+
* Resolve a model name to the Agent SDK model ID. The router hands over
|
|
135
|
+
* canonical catalog IDs; aliases are accepted for direct callers.
|
|
136
|
+
* @param {string} [modelName] - Catalog ID or alias; defaults to DEFAULT_MODEL
|
|
137
|
+
* @returns {string}
|
|
138
|
+
* @throws {ClaudeProviderError} When the name is not in the catalog
|
|
182
139
|
*/
|
|
183
140
|
function resolveSdkModel(modelName) {
|
|
184
141
|
if (typeof modelName !== 'string' || !modelName.trim()) {
|
|
185
|
-
return
|
|
142
|
+
return DEFAULT_MODEL;
|
|
186
143
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let name = modelName.trim();
|
|
194
|
-
if (name.toLowerCase().startsWith('claude:')) {
|
|
195
|
-
name = name.slice('claude:'.length).trim();
|
|
144
|
+
const id = findCatalogId(SUPPORTED_MODELS, modelName);
|
|
145
|
+
if (!id) {
|
|
146
|
+
throw new ClaudeProviderError(
|
|
147
|
+
`Unknown Claude model "${modelName}"`,
|
|
148
|
+
ErrorCodes.MODEL_NOT_FOUND,
|
|
149
|
+
);
|
|
196
150
|
}
|
|
197
|
-
return
|
|
151
|
+
return SUPPORTED_MODELS[id].modelName;
|
|
198
152
|
}
|
|
199
153
|
|
|
200
154
|
/**
|
|
@@ -338,7 +292,7 @@ async function* createStreamingGenerator(
|
|
|
338
292
|
try {
|
|
339
293
|
const queryOptions = {
|
|
340
294
|
...options,
|
|
341
|
-
model: options.model ||
|
|
295
|
+
model: options.model || DEFAULT_MODEL,
|
|
342
296
|
maxTurns: 100,
|
|
343
297
|
permissionMode: 'bypassPermissions', // Don't prompt for permissions
|
|
344
298
|
};
|
|
@@ -475,7 +429,7 @@ export const claudeProvider = {
|
|
|
475
429
|
*/
|
|
476
430
|
async invoke(messages, options = {}) {
|
|
477
431
|
const {
|
|
478
|
-
model =
|
|
432
|
+
model = DEFAULT_MODEL,
|
|
479
433
|
config,
|
|
480
434
|
stream = false,
|
|
481
435
|
signal,
|
|
@@ -616,16 +570,14 @@ export const claudeProvider = {
|
|
|
616
570
|
}
|
|
617
571
|
},
|
|
618
572
|
|
|
573
|
+
defaultModel: DEFAULT_MODEL,
|
|
574
|
+
|
|
619
575
|
/**
|
|
620
|
-
* Validate Claude SDK configuration
|
|
621
|
-
*
|
|
622
|
-
* Returns true optimistically - authentication errors handled at runtime
|
|
576
|
+
* Validate Claude SDK configuration: the SDK is installed and a Claude Code
|
|
577
|
+
* credential is present. Expired logins surface at invoke time.
|
|
623
578
|
*/
|
|
624
579
|
validateConfig(_config) {
|
|
625
|
-
|
|
626
|
-
// We can't reliably check auth status, so return true optimistically
|
|
627
|
-
// and let the SDK handle authentication errors during execution
|
|
628
|
-
return isClaudeSDKAvailable();
|
|
580
|
+
return isPackageResolvable(CLAUDE_SDK_PACKAGE) && hasClaudeCredentials();
|
|
629
581
|
},
|
|
630
582
|
|
|
631
583
|
/**
|
|
@@ -644,9 +596,8 @@ export const claudeProvider = {
|
|
|
644
596
|
|
|
645
597
|
/**
|
|
646
598
|
* Get model configuration for specific model
|
|
647
|
-
* Handles claude: prefixed names (e.g. "claude:opus", "claude:fable", "claude:opus-5")
|
|
648
599
|
*/
|
|
649
600
|
getModelConfig(modelName) {
|
|
650
|
-
return
|
|
601
|
+
return findCatalogEntry(SUPPORTED_MODELS, modelName);
|
|
651
602
|
},
|
|
652
603
|
};
|
package/src/providers/codex.js
CHANGED
|
@@ -20,10 +20,15 @@ import {
|
|
|
20
20
|
EFFORT_LADDER,
|
|
21
21
|
clampReasoningEffort,
|
|
22
22
|
} from '../utils/reasoningEffort.js';
|
|
23
|
+
import { findCatalogEntry, findCatalogId } from '../utils/modelCatalog.js';
|
|
24
|
+
import {
|
|
25
|
+
hasCodexCredentials,
|
|
26
|
+
isPackageResolvable,
|
|
27
|
+
} from '../utils/localProviderAuth.js';
|
|
23
28
|
|
|
24
29
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
30
|
+
* Models Codex can run, keyed by the slug passed to the CLI as --model. The
|
|
31
|
+
* catalog key is the canonical model ID the router resolves to. The reasoning tiers are the ones each model's API accepts, verified
|
|
27
32
|
* against the API's own rejection messages (gpt-6-astra: "Supported values
|
|
28
33
|
* are: 'low', 'medium', 'high', 'xhigh', and 'max'"; the Sol/Luna tiers of
|
|
29
34
|
* both generations accept 'none' as well). The SDK's ModelReasoningEffort
|
|
@@ -38,68 +43,56 @@ import {
|
|
|
38
43
|
* it reasons — so it is deliberately absent from every supportedEfforts list
|
|
39
44
|
* and nothing at the tool level can select it.
|
|
40
45
|
*/
|
|
41
|
-
const
|
|
42
|
-
|
|
46
|
+
const ALL_EFFORTS = ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
47
|
+
|
|
48
|
+
function codexModel(slug, friendlyName, { aliases, contextWindow = 272000, supportedEfforts = ALL_EFFORTS }) {
|
|
49
|
+
return {
|
|
50
|
+
modelName: slug,
|
|
51
|
+
friendlyName: `OpenAI Codex (${friendlyName})`,
|
|
52
|
+
contextWindow,
|
|
53
|
+
maxOutputTokens: 128000,
|
|
54
|
+
supportsStreaming: true,
|
|
55
|
+
supportsImages: true, // Codex SDK 0.118+ supports images via --image (local_image input)
|
|
56
|
+
supportsWebSearch: false, // Codex accesses files directly, not web
|
|
57
|
+
timeout: 1800000, // 30 minutes
|
|
58
|
+
description: `${friendlyName} via the Codex agentic coding assistant, with local file access and tool execution`,
|
|
59
|
+
aliases,
|
|
60
|
+
supportedEfforts,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const SUPPORTED_MODELS = {
|
|
65
|
+
'gpt-6-sol': codexModel('gpt-6-sol', 'GPT-6 Sol', {
|
|
43
66
|
aliases: ['sol', 'gpt-6', 'gpt6', 'gpt6-sol', 'gpt-6-codex'],
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
'gpt-6-luna': {
|
|
67
|
+
}),
|
|
68
|
+
'gpt-6-luna': codexModel('gpt-6-luna', 'GPT-6 Luna', {
|
|
48
69
|
aliases: ['luna', 'gpt6-luna'],
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
'gpt-6-astra': {
|
|
70
|
+
}),
|
|
71
|
+
'gpt-6-astra': codexModel('gpt-6-astra', 'GPT-6 Astra', {
|
|
53
72
|
aliases: ['astra', 'gpt6-astra'],
|
|
54
|
-
contextWindow: 272000,
|
|
55
73
|
supportedEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
56
|
-
},
|
|
57
|
-
'gpt-5.6-sol': {
|
|
74
|
+
}),
|
|
75
|
+
'gpt-5.6-sol': codexModel('gpt-5.6-sol', 'GPT-5.6 Sol', {
|
|
58
76
|
aliases: ['gpt-5.6', 'gpt5.6', 'gpt5.6-sol', 'gpt-5.6-codex'],
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
'gpt-5.6-terra': {
|
|
77
|
+
}),
|
|
78
|
+
'gpt-5.6-terra': codexModel('gpt-5.6-terra', 'GPT-5.6 Terra', {
|
|
63
79
|
aliases: ['terra', 'gpt5.6-terra'],
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
'gpt-5.6-luna': {
|
|
80
|
+
}),
|
|
81
|
+
'gpt-5.6-luna': codexModel('gpt-5.6-luna', 'GPT-5.6 Luna', {
|
|
68
82
|
aliases: ['gpt5.6-luna'],
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
72
|
-
'gpt-5.5': {
|
|
83
|
+
}),
|
|
84
|
+
'gpt-5.5': codexModel('gpt-5.5', 'GPT-5.5', {
|
|
73
85
|
aliases: ['gpt5.5'],
|
|
74
|
-
contextWindow: 272000,
|
|
75
86
|
supportedEfforts: ['low', 'medium', 'high', 'xhigh'],
|
|
76
|
-
},
|
|
77
|
-
'gpt-5.3-codex-spark': {
|
|
87
|
+
}),
|
|
88
|
+
'gpt-5.3-codex-spark': codexModel('gpt-5.3-codex-spark', 'GPT-5.3 Codex Spark', {
|
|
78
89
|
aliases: ['spark', 'codex-spark'],
|
|
79
90
|
contextWindow: 128000,
|
|
80
91
|
supportedEfforts: ['low', 'medium', 'high', 'xhigh'],
|
|
81
|
-
},
|
|
92
|
+
}),
|
|
82
93
|
};
|
|
83
94
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
// The single user-facing model the router exposes. The backend model behind it
|
|
87
|
-
// comes from CODEX_MODEL, or from a `codex:<model>` spec.
|
|
88
|
-
const SUPPORTED_MODELS = {
|
|
89
|
-
codex: {
|
|
90
|
-
modelName: 'codex',
|
|
91
|
-
friendlyName: 'OpenAI Codex (GPT-6 Sol)',
|
|
92
|
-
contextWindow: CODEX_BACKEND_MODELS[DEFAULT_BACKEND_MODEL].contextWindow,
|
|
93
|
-
maxOutputTokens: 128000,
|
|
94
|
-
supportsStreaming: true,
|
|
95
|
-
supportsImages: true, // Codex SDK 0.118+ supports images via --image (local_image input)
|
|
96
|
-
supportsWebSearch: false, // Codex accesses files directly, not web
|
|
97
|
-
timeout: 1800000, // 30 minutes
|
|
98
|
-
description:
|
|
99
|
-
'OpenAI Codex agentic coding assistant with local file access and tool execution (GPT-6 Sol by default; pick another backend with codex:<model> or CODEX_MODEL)',
|
|
100
|
-
aliases: [],
|
|
101
|
-
},
|
|
102
|
-
};
|
|
95
|
+
const DEFAULT_MODEL = 'gpt-6-sol';
|
|
103
96
|
|
|
104
97
|
/**
|
|
105
98
|
* Custom error class for Codex provider errors
|
|
@@ -111,26 +104,14 @@ class CodexProviderError extends ProviderError {
|
|
|
111
104
|
}
|
|
112
105
|
}
|
|
113
106
|
|
|
114
|
-
|
|
115
|
-
* Check if Codex SDK is available (optional dependency)
|
|
116
|
-
* Uses import.meta.resolve when available, falls back to filesystem check
|
|
117
|
-
*/
|
|
118
|
-
function isCodexAvailable() {
|
|
119
|
-
try {
|
|
120
|
-
// Just try to dynamically check if we can import it
|
|
121
|
-
// This is a simple presence check that works in ES modules
|
|
122
|
-
return true; // If SDK not available, the actual import() will fail later with clear error
|
|
123
|
-
} catch {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
107
|
+
const CODEX_SDK_PACKAGE = '@openai/codex-sdk';
|
|
127
108
|
|
|
128
109
|
/**
|
|
129
110
|
* Dynamically import Codex SDK (lazy loading)
|
|
130
111
|
* This keeps the SDK as an optional dependency
|
|
131
112
|
*/
|
|
132
113
|
async function getCodexSDK() {
|
|
133
|
-
if (!
|
|
114
|
+
if (!isPackageResolvable(CODEX_SDK_PACKAGE)) {
|
|
134
115
|
throw new CodexProviderError(
|
|
135
116
|
'Codex SDK not installed. Install with: npm install @openai/codex-sdk',
|
|
136
117
|
'CODEX_NOT_INSTALLED',
|
|
@@ -248,54 +229,24 @@ function extractPromptText(input) {
|
|
|
248
229
|
}
|
|
249
230
|
|
|
250
231
|
/**
|
|
251
|
-
* Resolve a
|
|
252
|
-
*
|
|
253
|
-
* @
|
|
232
|
+
* Resolve a model name to the slug passed to the CLI. The router hands over
|
|
233
|
+
* canonical catalog IDs; aliases are accepted for direct callers.
|
|
234
|
+
* @param {string} [model] - Catalog ID or alias; defaults to DEFAULT_MODEL
|
|
235
|
+
* @returns {string} Backend slug for the SDK's `model` option
|
|
236
|
+
* @throws {CodexProviderError} When the name is not in the catalog
|
|
254
237
|
*/
|
|
255
|
-
function
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return null;
|
|
238
|
+
export function resolveBackendModel(model) {
|
|
239
|
+
if (model === undefined || model === null || model === '') {
|
|
240
|
+
return DEFAULT_MODEL;
|
|
259
241
|
}
|
|
260
|
-
|
|
261
|
-
|
|
242
|
+
const slug = findCatalogId(SUPPORTED_MODELS, model);
|
|
243
|
+
if (!slug) {
|
|
244
|
+
throw new CodexProviderError(
|
|
245
|
+
`Unknown Codex model "${model}"`,
|
|
246
|
+
ErrorCodes.MODEL_NOT_FOUND,
|
|
247
|
+
);
|
|
262
248
|
}
|
|
263
|
-
return
|
|
264
|
-
Object.keys(CODEX_BACKEND_MODELS).find((slug) =>
|
|
265
|
-
CODEX_BACKEND_MODELS[slug].aliases.includes(lower),
|
|
266
|
-
) || null
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Catalog entry for a backend model, or null when the slug is not catalogued.
|
|
272
|
-
* @param {string} name - Backend slug or alias
|
|
273
|
-
* @returns {{ slug: string, aliases: string[], contextWindow: number, supportedEfforts: string[] }|null}
|
|
274
|
-
*/
|
|
275
|
-
export function getBackendModelConfig(name) {
|
|
276
|
-
const slug = findBackendSlug(name);
|
|
277
|
-
return slug ? { slug, ...CODEX_BACKEND_MODELS[slug] } : null;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Resolve the requested model spec to the backend slug passed to the CLI.
|
|
282
|
-
*
|
|
283
|
-
* `codex` uses CODEX_MODEL (default gpt-6-sol); `codex:<model>` names a
|
|
284
|
-
* backend directly, by slug or alias. Unknown names pass through verbatim so a
|
|
285
|
-
* newly released model works before it is catalogued here — the CLI rejects
|
|
286
|
-
* anything the backend does not know.
|
|
287
|
-
*
|
|
288
|
-
* @param {string} spec - Requested model, e.g. 'codex' or 'codex:sol'
|
|
289
|
-
* @param {Object} [config] - Loaded configuration
|
|
290
|
-
* @returns {string} Backend slug for the SDK's `model` option
|
|
291
|
-
*/
|
|
292
|
-
export function resolveBackendModel(spec, config) {
|
|
293
|
-
const raw = String(spec || '').trim();
|
|
294
|
-
const requested = raw.toLowerCase().startsWith('codex:')
|
|
295
|
-
? raw.slice('codex:'.length).trim()
|
|
296
|
-
: '';
|
|
297
|
-
const name = requested || config?.providers?.codexmodel || DEFAULT_BACKEND_MODEL;
|
|
298
|
-
return findBackendSlug(name) || name;
|
|
249
|
+
return slug;
|
|
299
250
|
}
|
|
300
251
|
|
|
301
252
|
/**
|
|
@@ -319,30 +270,6 @@ async function getThreadIdFromContinuation(
|
|
|
319
270
|
}
|
|
320
271
|
}
|
|
321
272
|
|
|
322
|
-
/**
|
|
323
|
-
* Resolve a user-facing model name to its entry in SUPPORTED_MODELS.
|
|
324
|
-
* @param {string} modelName
|
|
325
|
-
* @returns {Object|null}
|
|
326
|
-
*/
|
|
327
|
-
function findModelConfig(modelName) {
|
|
328
|
-
const modelNameLower = String(modelName || '').toLowerCase();
|
|
329
|
-
|
|
330
|
-
if (SUPPORTED_MODELS[modelNameLower]) {
|
|
331
|
-
return SUPPORTED_MODELS[modelNameLower];
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
for (const config of Object.values(SUPPORTED_MODELS)) {
|
|
335
|
-
if (
|
|
336
|
-
config.aliases &&
|
|
337
|
-
config.aliases.some((alias) => alias.toLowerCase() === modelNameLower)
|
|
338
|
-
) {
|
|
339
|
-
return config;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
273
|
/**
|
|
347
274
|
* Create stream generator for Codex streaming responses.
|
|
348
275
|
* `input` is the Codex SDK Input (string | UserInput[]) — strings for plain
|
|
@@ -382,7 +309,7 @@ export const codexProvider = {
|
|
|
382
309
|
*/
|
|
383
310
|
async invoke(messages, options = {}) {
|
|
384
311
|
const {
|
|
385
|
-
model =
|
|
312
|
+
model = DEFAULT_MODEL,
|
|
386
313
|
config,
|
|
387
314
|
stream = false,
|
|
388
315
|
signal,
|
|
@@ -400,6 +327,10 @@ export const codexProvider = {
|
|
|
400
327
|
);
|
|
401
328
|
}
|
|
402
329
|
|
|
330
|
+
// Resolved outside the try so an unknown model keeps MODEL_NOT_FOUND
|
|
331
|
+
// instead of being rewrapped as a generic execution failure.
|
|
332
|
+
const backendModel = resolveBackendModel(model);
|
|
333
|
+
|
|
403
334
|
try {
|
|
404
335
|
// Get Codex SDK
|
|
405
336
|
const Codex = await getCodexSDK();
|
|
@@ -456,7 +387,6 @@ export const codexProvider = {
|
|
|
456
387
|
const approvalPolicy = config.providers?.codexapprovalpolicy || 'never';
|
|
457
388
|
|
|
458
389
|
// Create or resume thread
|
|
459
|
-
const backendModel = resolveBackendModel(model, config);
|
|
460
390
|
const threadOptions = {
|
|
461
391
|
model: backendModel,
|
|
462
392
|
workingDirectory,
|
|
@@ -467,7 +397,7 @@ export const codexProvider = {
|
|
|
467
397
|
|
|
468
398
|
if (reasoning_effort) {
|
|
469
399
|
const supportedEfforts =
|
|
470
|
-
|
|
400
|
+
SUPPORTED_MODELS[backendModel]?.supportedEfforts || EFFORT_LADDER;
|
|
471
401
|
const mappedEffort = clampReasoningEffort(reasoning_effort, supportedEfforts);
|
|
472
402
|
threadOptions.modelReasoningEffort = mappedEffort;
|
|
473
403
|
if (mappedEffort !== reasoning_effort) {
|
|
@@ -575,15 +505,14 @@ export const codexProvider = {
|
|
|
575
505
|
}
|
|
576
506
|
},
|
|
577
507
|
|
|
508
|
+
defaultModel: DEFAULT_MODEL,
|
|
509
|
+
|
|
578
510
|
/**
|
|
579
|
-
* Validate Codex configuration
|
|
580
|
-
*
|
|
511
|
+
* Validate Codex configuration: the SDK is installed and a credential is
|
|
512
|
+
* present (ChatGPT login file or CODEX_API_KEY — NOT OPENAI_API_KEY).
|
|
581
513
|
*/
|
|
582
|
-
validateConfig(
|
|
583
|
-
|
|
584
|
-
// Since we can't reliably check ChatGPT login status, we'll be permissive
|
|
585
|
-
// and let the SDK handle authentication errors
|
|
586
|
-
return isCodexAvailable();
|
|
514
|
+
validateConfig(config) {
|
|
515
|
+
return isPackageResolvable(CODEX_SDK_PACKAGE) && hasCodexCredentials(config);
|
|
587
516
|
},
|
|
588
517
|
|
|
589
518
|
/**
|
|
@@ -604,6 +533,6 @@ export const codexProvider = {
|
|
|
604
533
|
* Get model configuration for specific model
|
|
605
534
|
*/
|
|
606
535
|
getModelConfig(modelName) {
|
|
607
|
-
return
|
|
536
|
+
return findCatalogEntry(SUPPORTED_MODELS, modelName);
|
|
608
537
|
},
|
|
609
538
|
};
|