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/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
|
};
|
package/src/providers/copilot.js
CHANGED
|
@@ -18,29 +18,19 @@ import { fileURLToPath } from 'node:url';
|
|
|
18
18
|
import { debugLog, debugError } from '../utils/console.js';
|
|
19
19
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
20
20
|
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
21
|
+
import { findCatalogEntry, findCatalogId } from '../utils/modelCatalog.js';
|
|
22
|
+
import { isPackageResolvable } from '../utils/localProviderAuth.js';
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
copilot: {
|
|
24
|
-
modelName: 'copilot',
|
|
25
|
-
friendlyName: 'GitHub Copilot (via CLI SDK)',
|
|
26
|
-
contextWindow: 128000,
|
|
27
|
-
maxOutputTokens: 16384,
|
|
28
|
-
supportsStreaming: true,
|
|
29
|
-
supportsImages: false,
|
|
30
|
-
supportsWebSearch: false,
|
|
31
|
-
timeout: 1800000,
|
|
32
|
-
description:
|
|
33
|
-
'GitHub Copilot via CLI SDK - uses default or env-configured model',
|
|
34
|
-
aliases: ['copilot-sdk', 'github-copilot'],
|
|
35
|
-
},
|
|
24
|
+
const DEFAULT_MODEL = 'gpt-6-sol';
|
|
36
25
|
|
|
26
|
+
// Keyed by the SDK model ID, which is also the canonical ID the router
|
|
27
|
+
// resolves to. Every name here is reached only through the `copilot:`
|
|
28
|
+
// namespace — Copilot never serves bare model names.
|
|
29
|
+
const SUPPORTED_MODELS = {
|
|
37
30
|
// OpenAI models
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// latest GPT tier (reachable only via the `copilot:` namespace — bare
|
|
42
|
-
// `codex` routes to the Codex provider and bare `gpt*` keyword-routes to
|
|
43
|
-
// OpenAI before Copilot's catalog is consulted).
|
|
31
|
+
// `gpt-6` / `gpt-5.6` point at that generation's Sol, matching Copilot's own
|
|
32
|
+
// bare-alias behavior; `sol`/`luna` and the legacy `gpt-5` shortcut follow
|
|
33
|
+
// the current generation. `codex` and `gpt` point at the latest GPT tier.
|
|
44
34
|
'gpt-6-sol': {
|
|
45
35
|
modelName: 'gpt-6-sol',
|
|
46
36
|
friendlyName: 'GPT-6 Sol (via Copilot)',
|
|
@@ -205,22 +195,6 @@ class CopilotProviderError extends ProviderError {
|
|
|
205
195
|
}
|
|
206
196
|
}
|
|
207
197
|
|
|
208
|
-
/**
|
|
209
|
-
* Check if Copilot SDK is available (installed as dependency)
|
|
210
|
-
*/
|
|
211
|
-
let _sdkAvailable = null;
|
|
212
|
-
function isCopilotSDKAvailable() {
|
|
213
|
-
if (_sdkAvailable !== null) return _sdkAvailable;
|
|
214
|
-
try {
|
|
215
|
-
// Use synchronous resolve to check if the package exists
|
|
216
|
-
import.meta.resolve('@github/copilot-sdk');
|
|
217
|
-
_sdkAvailable = true;
|
|
218
|
-
} catch {
|
|
219
|
-
_sdkAvailable = false;
|
|
220
|
-
}
|
|
221
|
-
return _sdkAvailable;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
198
|
/**
|
|
225
199
|
* Dynamically import Copilot SDK (lazy loading)
|
|
226
200
|
*/
|
|
@@ -443,106 +417,24 @@ function createPermissionHandler(accessLevel) {
|
|
|
443
417
|
}
|
|
444
418
|
|
|
445
419
|
/**
|
|
446
|
-
* Resolve a
|
|
447
|
-
*
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const lower = name.toLowerCase().trim();
|
|
452
|
-
if (!lower) return null;
|
|
453
|
-
|
|
454
|
-
// Direct key match
|
|
455
|
-
if (SUPPORTED_MODELS[lower] && lower !== 'copilot') {
|
|
456
|
-
return SUPPORTED_MODELS[lower].modelName;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Alias match
|
|
460
|
-
for (const config of Object.values(SUPPORTED_MODELS)) {
|
|
461
|
-
if (config.modelName === 'copilot') continue;
|
|
462
|
-
if (
|
|
463
|
-
config.aliases &&
|
|
464
|
-
config.aliases.some((alias) => alias.toLowerCase() === lower)
|
|
465
|
-
) {
|
|
466
|
-
return config.modelName;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Look up model config from SUPPORTED_MODELS by name or alias.
|
|
475
|
-
* Strips copilot: prefix and falls back to the base copilot config.
|
|
476
|
-
*/
|
|
477
|
-
function findModelConfig(modelName) {
|
|
478
|
-
if (typeof modelName !== 'string') return null;
|
|
479
|
-
|
|
480
|
-
let name = modelName;
|
|
481
|
-
if (name.toLowerCase().startsWith('copilot:')) {
|
|
482
|
-
name = name.slice('copilot:'.length).trim();
|
|
483
|
-
}
|
|
484
|
-
if (!name) return SUPPORTED_MODELS.copilot;
|
|
485
|
-
|
|
486
|
-
const nameLower = name.toLowerCase();
|
|
487
|
-
|
|
488
|
-
if (SUPPORTED_MODELS[nameLower]) {
|
|
489
|
-
return SUPPORTED_MODELS[nameLower];
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
for (const config of Object.values(SUPPORTED_MODELS)) {
|
|
493
|
-
if (
|
|
494
|
-
config.aliases &&
|
|
495
|
-
config.aliases.some((alias) => alias.toLowerCase() === nameLower)
|
|
496
|
-
) {
|
|
497
|
-
return config;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return null;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Resolve model to pass to SDK session
|
|
506
|
-
* Precedence: explicit model param > config COPILOT_MODEL > omit (SDK default)
|
|
507
|
-
*
|
|
508
|
-
* Handles copilot: prefix stripping, alias resolution, and env var fallback.
|
|
509
|
-
* Note: "copilot" is a Converse routing alias, not a valid SDK model ID.
|
|
420
|
+
* Resolve a model name to the SDK model ID for the session. The router hands
|
|
421
|
+
* over canonical catalog IDs; aliases are accepted for direct callers.
|
|
422
|
+
* @param {string} [model] - Catalog ID or alias; defaults to DEFAULT_MODEL
|
|
423
|
+
* @returns {string}
|
|
424
|
+
* @throws {CopilotProviderError} When the name is not in the catalog
|
|
510
425
|
*/
|
|
511
|
-
function resolveSessionModel(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
// Guard non-string inputs
|
|
515
|
-
if (typeof requestModel !== 'string') {
|
|
516
|
-
requestModel = '';
|
|
426
|
+
function resolveSessionModel(model) {
|
|
427
|
+
if (typeof model !== 'string' || !model.trim()) {
|
|
428
|
+
return DEFAULT_MODEL;
|
|
517
429
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// Empty suffix or converse alias → use env/default
|
|
526
|
-
if (
|
|
527
|
-
!effectiveModel ||
|
|
528
|
-
converseAliases.includes(effectiveModel.toLowerCase())
|
|
529
|
-
) {
|
|
530
|
-
const envModel = config?.providers?.copilotmodel;
|
|
531
|
-
if (envModel) {
|
|
532
|
-
let resolved = typeof envModel === 'string' ? envModel : '';
|
|
533
|
-
if (resolved.toLowerCase().startsWith('copilot:')) {
|
|
534
|
-
resolved = resolved.slice('copilot:'.length).trim();
|
|
535
|
-
}
|
|
536
|
-
if (!resolved || converseAliases.includes(resolved.toLowerCase())) {
|
|
537
|
-
return undefined;
|
|
538
|
-
}
|
|
539
|
-
return resolveModelAlias(resolved) || resolved;
|
|
540
|
-
}
|
|
541
|
-
return undefined;
|
|
430
|
+
const id = findCatalogId(SUPPORTED_MODELS, model);
|
|
431
|
+
if (!id) {
|
|
432
|
+
throw new CopilotProviderError(
|
|
433
|
+
`Unknown Copilot model "${model}"`,
|
|
434
|
+
ErrorCodes.MODEL_NOT_FOUND,
|
|
435
|
+
);
|
|
542
436
|
}
|
|
543
|
-
|
|
544
|
-
// Resolve alias or passthrough unknown models to SDK
|
|
545
|
-
return resolveModelAlias(effectiveModel) || effectiveModel;
|
|
437
|
+
return SUPPORTED_MODELS[id].modelName;
|
|
546
438
|
}
|
|
547
439
|
|
|
548
440
|
/**
|
|
@@ -590,23 +482,20 @@ async function checkReasoningSupport(client, modelId) {
|
|
|
590
482
|
async function* createStreamingGenerator(client, prompt, options, signal, config) {
|
|
591
483
|
const { model, timeout = 1800000, reasoning_effort } = options;
|
|
592
484
|
|
|
593
|
-
const sessionModel = resolveSessionModel(model
|
|
485
|
+
const sessionModel = resolveSessionModel(model);
|
|
594
486
|
const accessLevel = getToolAccessLevel(config);
|
|
595
487
|
|
|
596
488
|
const sessionConfig = {
|
|
597
489
|
streaming: true,
|
|
598
490
|
onPermissionRequest: createPermissionHandler(accessLevel),
|
|
491
|
+
model: sessionModel,
|
|
599
492
|
};
|
|
600
493
|
|
|
601
|
-
if (sessionModel) {
|
|
602
|
-
sessionConfig.model = sessionModel;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
494
|
if (reasoning_effort) {
|
|
606
495
|
const mapped = mapReasoningEffort(reasoning_effort);
|
|
607
496
|
if (mapped) {
|
|
608
|
-
const effectiveModel = sessionModel
|
|
609
|
-
const modelDef =
|
|
497
|
+
const effectiveModel = sessionModel;
|
|
498
|
+
const modelDef = SUPPORTED_MODELS[sessionModel];
|
|
610
499
|
|
|
611
500
|
let supported;
|
|
612
501
|
if (modelDef && modelDef.supportsReasoningEffort !== undefined) {
|
|
@@ -650,7 +539,7 @@ async function* createStreamingGenerator(client, prompt, options, signal, config
|
|
|
650
539
|
yield {
|
|
651
540
|
type: 'start',
|
|
652
541
|
provider: 'copilot',
|
|
653
|
-
model: sessionModel
|
|
542
|
+
model: sessionModel,
|
|
654
543
|
};
|
|
655
544
|
|
|
656
545
|
// Bridge push-based SDK events to pull-based generator using queue + promise
|
|
@@ -775,7 +664,7 @@ async function* createStreamingGenerator(client, prompt, options, signal, config
|
|
|
775
664
|
}
|
|
776
665
|
}
|
|
777
666
|
|
|
778
|
-
export {
|
|
667
|
+
export { resolveSessionModel, resolveCopilotCliPath };
|
|
779
668
|
|
|
780
669
|
/**
|
|
781
670
|
* Copilot SDK Provider Implementation
|
|
@@ -783,7 +672,7 @@ export { resolveModelAlias, resolveSessionModel, resolveCopilotCliPath };
|
|
|
783
672
|
export const copilotProvider = {
|
|
784
673
|
async invoke(messages, options = {}) {
|
|
785
674
|
const {
|
|
786
|
-
model =
|
|
675
|
+
model = DEFAULT_MODEL,
|
|
787
676
|
config,
|
|
788
677
|
stream = false,
|
|
789
678
|
signal,
|
|
@@ -797,15 +686,17 @@ export const copilotProvider = {
|
|
|
797
686
|
);
|
|
798
687
|
}
|
|
799
688
|
|
|
689
|
+
// Validated before the client spawns the Copilot CLI.
|
|
690
|
+
const sessionModel = resolveSessionModel(model);
|
|
691
|
+
|
|
800
692
|
try {
|
|
801
693
|
const cwd = config.server?.client_cwd || process.cwd();
|
|
802
694
|
const client = await getCopilotClient(cwd, config);
|
|
803
695
|
const prompt = convertMessagesToPrompt(messages);
|
|
804
696
|
|
|
805
|
-
const
|
|
806
|
-
const modelConfig = findModelConfig(sessionModel || model) || SUPPORTED_MODELS.copilot;
|
|
697
|
+
const modelConfig = SUPPORTED_MODELS[sessionModel];
|
|
807
698
|
const invokeOptions = {
|
|
808
|
-
model,
|
|
699
|
+
model: sessionModel,
|
|
809
700
|
timeout: modelConfig.timeout,
|
|
810
701
|
reasoning_effort,
|
|
811
702
|
};
|
|
@@ -843,7 +734,7 @@ export const copilotProvider = {
|
|
|
843
734
|
rawResponse: { content, usage },
|
|
844
735
|
metadata: {
|
|
845
736
|
provider: 'copilot',
|
|
846
|
-
model: sessionModel
|
|
737
|
+
model: sessionModel,
|
|
847
738
|
usage: usage
|
|
848
739
|
? {
|
|
849
740
|
input_tokens: usage.input_tokens || 0,
|
|
@@ -901,12 +792,14 @@ export const copilotProvider = {
|
|
|
901
792
|
}
|
|
902
793
|
},
|
|
903
794
|
|
|
795
|
+
defaultModel: DEFAULT_MODEL,
|
|
796
|
+
|
|
904
797
|
/**
|
|
905
798
|
* Validate Copilot SDK configuration
|
|
906
799
|
* Returns true optimistically — auth errors surface at runtime
|
|
907
800
|
*/
|
|
908
801
|
validateConfig(_config) {
|
|
909
|
-
return
|
|
802
|
+
return isPackageResolvable('@github/copilot-sdk');
|
|
910
803
|
},
|
|
911
804
|
|
|
912
805
|
isAvailable(config) {
|
|
@@ -918,6 +811,6 @@ export const copilotProvider = {
|
|
|
918
811
|
},
|
|
919
812
|
|
|
920
813
|
getModelConfig(modelName) {
|
|
921
|
-
return
|
|
814
|
+
return findCatalogEntry(SUPPORTED_MODELS, modelName);
|
|
922
815
|
},
|
|
923
816
|
};
|
|
@@ -140,6 +140,7 @@ export const deepseekProvider = createOpenAICompatibleProvider({
|
|
|
140
140
|
baseURL: 'https://api.deepseek.com',
|
|
141
141
|
providerName: 'DeepSeek',
|
|
142
142
|
supportedModels: SUPPORTED_MODELS,
|
|
143
|
+
defaultModel: 'deepseek-v4-pro',
|
|
143
144
|
validateApiKey,
|
|
144
145
|
transformRequest,
|
|
145
146
|
transformResponse,
|