@cloudcli-ai/cloudcli 1.35.0 → 1.36.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/README.md +12 -7
- package/dist/api-docs.html +8 -2
- package/dist/assets/{index-CtwxqzGG.js → index-S6cIy7hj.js} +271 -260
- package/dist/assets/index-uLUE6Qt1.css +32 -0
- package/dist/index.html +12 -4
- package/dist-server/server/claude-sdk.js +25 -26
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/index.js +0 -13
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +70 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +52 -11
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +138 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +58 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +0 -5
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
- package/dist-server/server/openai-codex.js +9 -8
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +20 -2
- package/dist-server/server/opencode-cli.js.map +1 -1
- package/dist-server/server/routes/agent.js +14 -3
- package/dist-server/server/routes/agent.js.map +1 -1
- package/electron/desktopWindow.js +0 -15
- package/electron/launcher/launcher.js +1 -143
- package/electron/main.js +1 -117
- package/electron/preload.cjs +0 -3
- package/package.json +3 -8
- package/public/api-docs.html +8 -2
- package/scripts/release/prepare-desktop-app.js +0 -6
- package/server/claude-sdk.js +32 -26
- package/server/index.js +0 -14
- package/server/modules/providers/list/claude/claude-models.provider.ts +73 -1
- package/server/modules/providers/list/codex/codex-models.provider.ts +59 -11
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +180 -1
- package/server/modules/providers/services/provider-capabilities.service.ts +7 -0
- package/server/modules/providers/services/provider-models.service.ts +1 -1
- package/server/modules/providers/tests/opencode-models.test.ts +62 -0
- package/server/modules/websocket/services/websocket-server.service.ts +0 -6
- package/server/openai-codex.js +9 -7
- package/server/opencode-cli.js +21 -2
- package/server/routes/agent.js +14 -3
- package/server/shared/types.ts +7 -0
- package/dist/assets/index-DlvtxFyt.css +0 -32
- package/dist-server/server/computer-use-agent.js +0 -242
- package/dist-server/server/computer-use-agent.js.map +0 -1
- package/dist-server/server/computer-use-mcp.js +0 -519
- package/dist-server/server/computer-use-mcp.js.map +0 -1
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js +0 -55
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js.map +0 -1
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js +0 -2
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-executor.js +0 -212
- package/dist-server/server/modules/computer-use/computer-executor.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-semantics.service.js +0 -422
- package/dist-server/server/modules/computer-use/computer-semantics.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js +0 -126
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use.routes.js +0 -196
- package/dist-server/server/modules/computer-use/computer-use.routes.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use.service.js +0 -759
- package/dist-server/server/modules/computer-use/computer-use.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js +0 -126
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/index.js +0 -3
- package/dist-server/server/modules/computer-use/index.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js +0 -59
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js +0 -5
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js +0 -2
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js +0 -5
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js +0 -106
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js +0 -78
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js +0 -68
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js +0 -16
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js +0 -2
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js.map +0 -1
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js +0 -39
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js.map +0 -1
- package/electron/computerAgent.js +0 -290
- package/scripts/build-computer-semantics.mjs +0 -133
- package/scripts/copy-computer-semantics-bin.mjs +0 -24
- package/server/computer-use-agent.ts +0 -279
- package/server/computer-use-mcp.ts +0 -574
- package/server/modules/computer-use/actions/raw-action-dispatcher.ts +0 -67
- package/server/modules/computer-use/actions/raw-action-types.ts +0 -28
- package/server/modules/computer-use/computer-executor.ts +0 -242
- package/server/modules/computer-use/computer-semantics.service.ts +0 -460
- package/server/modules/computer-use/computer-use-mcp.routes.ts +0 -141
- package/server/modules/computer-use/computer-use.routes.ts +0 -211
- package/server/modules/computer-use/computer-use.service.ts +0 -920
- package/server/modules/computer-use/desktop-agent-relay.service.ts +0 -158
- package/server/modules/computer-use/index.ts +0 -2
- package/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.ts +0 -82
- package/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.ts +0 -5
- package/server/modules/computer-use/semantics/adapters/semantic-adapter.ts +0 -23
- package/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.ts +0 -5
- package/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/helpers/macos/CloudCLISemantics.swift +0 -467
- package/server/modules/computer-use/semantics/helpers/semantic-helper-process.ts +0 -124
- package/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.ts +0 -97
- package/server/modules/computer-use/semantics/helpers/windows/CloudCLISemantics.csproj +0 -11
- package/server/modules/computer-use/semantics/helpers/windows/Program.cs +0 -534
- package/server/modules/computer-use/semantics/semantic-session-store.ts +0 -87
- package/server/modules/computer-use/semantics/semantic-tool-dispatcher.ts +0 -17
- package/server/modules/computer-use/semantics/semantic-types.ts +0 -58
- package/server/modules/websocket/services/desktop-agent-websocket.service.ts +0 -51
package/server/claude-sdk.js
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* - WebSocket message streaming
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
16
15
|
import crypto from 'crypto';
|
|
17
16
|
import { promises as fs } from 'fs';
|
|
18
|
-
import path from 'path';
|
|
19
17
|
import os from 'os';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
|
|
20
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
21
|
+
|
|
20
22
|
import { CLAUDE_FALLBACK_MODELS } from './modules/providers/list/claude/claude-models.provider.js';
|
|
21
23
|
import { providerModelsService } from './modules/providers/services/provider-models.service.js';
|
|
22
24
|
import { resolveClaudeCodeExecutablePath } from './shared/claude-cli-path.js';
|
|
@@ -41,6 +43,15 @@ const TOOL_APPROVAL_TIMEOUT_MS = parseInt(process.env.CLAUDE_TOOL_APPROVAL_TIMEO
|
|
|
41
43
|
|
|
42
44
|
const TOOLS_REQUIRING_INTERACTION = new Set(['AskUserQuestion', 'ExitPlanMode']);
|
|
43
45
|
|
|
46
|
+
function resolveClaudeEffort(model, effort, modelsDefinition = CLAUDE_FALLBACK_MODELS) {
|
|
47
|
+
const selectedModel = modelsDefinition?.OPTIONS?.find((option) => option.value === model) || null;
|
|
48
|
+
const allowedEfforts = selectedModel?.effort?.values
|
|
49
|
+
?.map((value) => value.value) || [];
|
|
50
|
+
return typeof effort === 'string' && effort !== 'default' && allowedEfforts.includes(effort)
|
|
51
|
+
? effort
|
|
52
|
+
: undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
function createRequestId() {
|
|
45
56
|
if (typeof crypto.randomUUID === 'function') {
|
|
46
57
|
return crypto.randomUUID();
|
|
@@ -145,13 +156,8 @@ function matchesToolPermission(entry, toolName, input) {
|
|
|
145
156
|
return false;
|
|
146
157
|
}
|
|
147
158
|
|
|
148
|
-
/**
|
|
149
|
-
* Maps CLI options to SDK-compatible options format
|
|
150
|
-
* @param {Object} options - CLI options
|
|
151
|
-
* @returns {Object} SDK-compatible options
|
|
152
|
-
*/
|
|
153
159
|
function mapCliOptionsToSDK(options = {}) {
|
|
154
|
-
const { sessionId, cwd, toolsSettings, permissionMode } = options;
|
|
160
|
+
const { sessionId, cwd, toolsSettings, permissionMode, effort } = options;
|
|
155
161
|
|
|
156
162
|
const sdkOptions = {};
|
|
157
163
|
|
|
@@ -163,32 +169,26 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
163
169
|
// which does not reliably follow npm's shell wrappers like cross-spawn does.
|
|
164
170
|
sdkOptions.pathToClaudeCodeExecutable = resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH);
|
|
165
171
|
|
|
166
|
-
// Map working directory
|
|
167
172
|
if (cwd) {
|
|
168
173
|
sdkOptions.cwd = cwd;
|
|
169
174
|
}
|
|
170
175
|
|
|
171
|
-
// Map permission mode
|
|
172
176
|
if (permissionMode && permissionMode !== 'default') {
|
|
173
177
|
sdkOptions.permissionMode = permissionMode;
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
// Map tool settings
|
|
177
180
|
const settings = toolsSettings || {
|
|
178
181
|
allowedTools: [],
|
|
179
182
|
disallowedTools: [],
|
|
180
183
|
skipPermissions: false
|
|
181
184
|
};
|
|
182
185
|
|
|
183
|
-
// Handle tool permissions
|
|
184
186
|
if (settings.skipPermissions && permissionMode !== 'plan') {
|
|
185
|
-
// When skipping permissions, use bypassPermissions mode
|
|
186
187
|
sdkOptions.permissionMode = 'bypassPermissions';
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
let allowedTools = [...(settings.allowedTools || [])];
|
|
190
191
|
|
|
191
|
-
// Add plan mode default tools
|
|
192
192
|
if (permissionMode === 'plan') {
|
|
193
193
|
const planModeTools = ['Read', 'Task', 'exit_plan_mode', 'TodoRead', 'TodoWrite', 'WebFetch', 'WebSearch'];
|
|
194
194
|
for (const tool of planModeTools) {
|
|
@@ -207,22 +207,24 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
207
207
|
|
|
208
208
|
sdkOptions.disallowedTools = settings.disallowedTools || [];
|
|
209
209
|
|
|
210
|
-
// Map model (default to sonnet)
|
|
211
|
-
// Valid models: sonnet, opus, haiku, opusplan, sonnet[1m], fable
|
|
212
210
|
sdkOptions.model = options.model || CLAUDE_FALLBACK_MODELS.DEFAULT;
|
|
213
|
-
// Model logged at query start below
|
|
214
211
|
|
|
215
|
-
|
|
212
|
+
const resolvedEffort = resolveClaudeEffort(
|
|
213
|
+
sdkOptions.model,
|
|
214
|
+
effort,
|
|
215
|
+
options.effortModels || CLAUDE_FALLBACK_MODELS,
|
|
216
|
+
);
|
|
217
|
+
if (resolvedEffort) {
|
|
218
|
+
sdkOptions.effort = resolvedEffort;
|
|
219
|
+
}
|
|
220
|
+
|
|
216
221
|
sdkOptions.systemPrompt = {
|
|
217
222
|
type: 'preset',
|
|
218
|
-
preset: 'claude_code'
|
|
223
|
+
preset: 'claude_code'
|
|
219
224
|
};
|
|
220
225
|
|
|
221
|
-
// Map setting sources for CLAUDE.md loading
|
|
222
|
-
// This loads CLAUDE.md from project, user (~/.config/claude/CLAUDE.md), and local directories
|
|
223
226
|
sdkOptions.settingSources = ['project', 'user', 'local'];
|
|
224
227
|
|
|
225
|
-
// Map resume session
|
|
226
228
|
if (sessionId) {
|
|
227
229
|
sdkOptions.resume = sessionId;
|
|
228
230
|
}
|
|
@@ -533,20 +535,24 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
533
535
|
sessionId,
|
|
534
536
|
options.model,
|
|
535
537
|
);
|
|
538
|
+
let effortModels = CLAUDE_FALLBACK_MODELS;
|
|
539
|
+
try {
|
|
540
|
+
effortModels = (await providerModelsService.getProviderModels('claude')).models;
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.warn('[Claude SDK] Unable to load provider models for effort validation:', error);
|
|
543
|
+
}
|
|
536
544
|
|
|
537
|
-
// Map CLI options to SDK format
|
|
538
545
|
const sdkOptions = mapCliOptionsToSDK({
|
|
539
546
|
...options,
|
|
540
547
|
model: resolvedModel || options.model,
|
|
548
|
+
effortModels,
|
|
541
549
|
});
|
|
542
550
|
|
|
543
|
-
// Load MCP configuration
|
|
544
551
|
const mcpServers = await loadMcpConfig(options.cwd);
|
|
545
552
|
if (mcpServers) {
|
|
546
553
|
sdkOptions.mcpServers = mcpServers;
|
|
547
554
|
}
|
|
548
555
|
|
|
549
|
-
// Handle images - save to temp files and modify prompt
|
|
550
556
|
const imageResult = await handleImages(command, options.images, options.cwd);
|
|
551
557
|
const finalCommand = imageResult.modifiedCommand;
|
|
552
558
|
tempImagePaths = imageResult.tempImagePaths;
|
|
@@ -650,7 +656,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
650
656
|
return { behavior: 'deny', message: decision.message ?? 'User denied tool use' };
|
|
651
657
|
};
|
|
652
658
|
|
|
653
|
-
//
|
|
659
|
+
// Query constructor reads this synchronously.
|
|
654
660
|
const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
|
|
655
661
|
process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';
|
|
656
662
|
|
package/server/index.js
CHANGED
|
@@ -66,9 +66,6 @@ import voiceRoutes from './voice-proxy.js';
|
|
|
66
66
|
import browserUseRoutes from './modules/browser-use/browser-use.routes.js';
|
|
67
67
|
import browserUseMcpRoutes from './modules/browser-use/browser-use-mcp.routes.js';
|
|
68
68
|
import { browserUseService } from './modules/browser-use/browser-use.service.js';
|
|
69
|
-
import computerUseRoutes from './modules/computer-use/computer-use.routes.js';
|
|
70
|
-
import computerUseMcpRoutes from './modules/computer-use/computer-use-mcp.routes.js';
|
|
71
|
-
import { computerUseService } from './modules/computer-use/computer-use.service.js';
|
|
72
69
|
import { startEnabledPluginServers, stopAllPlugins, getPluginPort } from './utils/plugin-process-manager.js';
|
|
73
70
|
import { initializeDatabase, projectsDb, sessionsDb } from './modules/database/index.js';
|
|
74
71
|
import { configureWebPush } from './services/vapid-keys.js';
|
|
@@ -223,12 +220,6 @@ app.use('/api/browser-use-mcp', browserUseMcpRoutes);
|
|
|
223
220
|
// Browser API Routes (protected)
|
|
224
221
|
app.use('/api/browser-use', authenticateToken, browserUseRoutes);
|
|
225
222
|
|
|
226
|
-
// Computer Use MCP bridge API (local token protected)
|
|
227
|
-
app.use('/api/computer-use-mcp', computerUseMcpRoutes);
|
|
228
|
-
|
|
229
|
-
// Computer Use API Routes (protected)
|
|
230
|
-
app.use('/api/computer-use', authenticateToken, computerUseRoutes);
|
|
231
|
-
|
|
232
223
|
// Unified provider MCP routes (protected)
|
|
233
224
|
app.use('/api/providers', authenticateToken, providerRoutes);
|
|
234
225
|
|
|
@@ -1785,11 +1776,6 @@ async function startServer() {
|
|
|
1785
1776
|
} catch (err) {
|
|
1786
1777
|
console.error('[Browser] Error stopping sessions during shutdown:', err?.message || err);
|
|
1787
1778
|
}
|
|
1788
|
-
try {
|
|
1789
|
-
await computerUseService.stopAllSessions();
|
|
1790
|
-
} catch (err) {
|
|
1791
|
-
console.error('[Computer Use] Error stopping sessions during shutdown:', err?.message || err);
|
|
1792
|
-
}
|
|
1793
1779
|
try {
|
|
1794
1780
|
await stopAllPlugins();
|
|
1795
1781
|
} catch (err) {
|
|
@@ -5,6 +5,7 @@ import type { IProviderModels } from '@/shared/interfaces.js';
|
|
|
5
5
|
import type {
|
|
6
6
|
ProviderChangeActiveModelInput,
|
|
7
7
|
ProviderCurrentActiveModel,
|
|
8
|
+
ProviderModelOption,
|
|
8
9
|
ProviderModelsDefinition,
|
|
9
10
|
ProviderSessionActiveModelChange,
|
|
10
11
|
} from '@/shared/types.js';
|
|
@@ -18,27 +19,89 @@ export const CLAUDE_FALLBACK_MODELS: ProviderModelsDefinition = {
|
|
|
18
19
|
{
|
|
19
20
|
value: 'default',
|
|
20
21
|
label: 'Default (recommended)',
|
|
21
|
-
description: 'Use the default model (currently
|
|
22
|
+
description: 'Use the Claude Code default model (currently Sonnet 4.6)',
|
|
23
|
+
effort: {
|
|
24
|
+
default: 'high',
|
|
25
|
+
values: [
|
|
26
|
+
{ value: 'low' },
|
|
27
|
+
{ value: 'medium' },
|
|
28
|
+
{ value: 'high' },
|
|
29
|
+
{ value: 'max' },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
22
32
|
},
|
|
23
33
|
{
|
|
24
34
|
value: 'fable',
|
|
25
35
|
label: 'Fable',
|
|
26
36
|
description: 'Fable 5 · Most capable for your hardest and longest-running tasks · Uses your limits ~2× faster than Opus',
|
|
37
|
+
effort: {
|
|
38
|
+
default: 'high',
|
|
39
|
+
values: [
|
|
40
|
+
{ value: 'low' },
|
|
41
|
+
{ value: 'medium' },
|
|
42
|
+
{ value: 'high' },
|
|
43
|
+
{ value: 'xhigh' },
|
|
44
|
+
{ value: 'max' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
27
47
|
},
|
|
28
48
|
{
|
|
29
49
|
value: "sonnet",
|
|
30
50
|
label: "Sonnet",
|
|
31
51
|
description: "Sonnet 4.6 · Best for everyday tasks · $3/$15 per Mtok",
|
|
52
|
+
effort: {
|
|
53
|
+
default: 'high',
|
|
54
|
+
values: [
|
|
55
|
+
{ value: 'low' },
|
|
56
|
+
{ value: 'medium' },
|
|
57
|
+
{ value: 'high' },
|
|
58
|
+
{ value: 'max' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
32
61
|
},
|
|
33
62
|
{
|
|
34
63
|
value: 'sonnet[1m]',
|
|
35
64
|
label: 'Sonnet (1M context)',
|
|
36
65
|
description: 'Sonnet 4.6 for long sessions · $3/$15 per Mtok',
|
|
66
|
+
effort: {
|
|
67
|
+
default: 'high',
|
|
68
|
+
values: [
|
|
69
|
+
{ value: 'low' },
|
|
70
|
+
{ value: 'medium' },
|
|
71
|
+
{ value: 'high' },
|
|
72
|
+
{ value: 'max' },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
value: 'opus',
|
|
78
|
+
label: 'Opus',
|
|
79
|
+
description: 'Opus 4.8 · Best for everyday, complex tasks · ~2× usage vs Sonnet',
|
|
80
|
+
effort: {
|
|
81
|
+
default: 'high',
|
|
82
|
+
values: [
|
|
83
|
+
{ value: 'low' },
|
|
84
|
+
{ value: 'medium' },
|
|
85
|
+
{ value: 'high' },
|
|
86
|
+
{ value: 'xhigh' },
|
|
87
|
+
{ value: 'max' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
37
90
|
},
|
|
38
91
|
{
|
|
39
92
|
value: 'opus[1m]',
|
|
40
93
|
label: 'Opus 4.8 (1M context)',
|
|
41
94
|
description: 'Opus 4.8 with 1M context · Most capable for complex work · $5/$25 per Mtok',
|
|
95
|
+
effort: {
|
|
96
|
+
default: 'high',
|
|
97
|
+
values: [
|
|
98
|
+
{ value: 'low' },
|
|
99
|
+
{ value: 'medium' },
|
|
100
|
+
{ value: 'high' },
|
|
101
|
+
{ value: 'xhigh' },
|
|
102
|
+
{ value: 'max' },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
42
105
|
},
|
|
43
106
|
{
|
|
44
107
|
value: 'haiku',
|
|
@@ -48,6 +111,15 @@ export const CLAUDE_FALLBACK_MODELS: ProviderModelsDefinition = {
|
|
|
48
111
|
],
|
|
49
112
|
DEFAULT: 'default',
|
|
50
113
|
};
|
|
114
|
+
|
|
115
|
+
export const findClaudeModelOption = (model: string | undefined | null): ProviderModelOption | null => {
|
|
116
|
+
const normalizedModel = typeof model === 'string' ? model.trim() : '';
|
|
117
|
+
if (!normalizedModel) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return CLAUDE_FALLBACK_MODELS.OPTIONS.find((option) => option.value === normalizedModel) ?? null;
|
|
122
|
+
};
|
|
51
123
|
type ClaudeInitEvent = {
|
|
52
124
|
sessionId?: string;
|
|
53
125
|
session_id?: string;
|
|
@@ -21,11 +21,30 @@ import {
|
|
|
21
21
|
|
|
22
22
|
export const CODEX_FALLBACK_MODELS: ProviderModelsDefinition = {
|
|
23
23
|
OPTIONS: [
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
{
|
|
25
|
+
value: 'gpt-5.5',
|
|
26
|
+
label: 'gpt-5.5',
|
|
27
|
+
effort: {
|
|
28
|
+
default: 'medium',
|
|
29
|
+
values: [{ value: 'low' }, { value: 'medium' }, { value: 'high' }, { value: 'xhigh' }],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: 'gpt-5.4',
|
|
34
|
+
label: 'gpt-5.4',
|
|
35
|
+
effort: {
|
|
36
|
+
default: 'medium',
|
|
37
|
+
values: [{ value: 'low' }, { value: 'medium' }, { value: 'high' }, { value: 'xhigh' }],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: 'gpt-5.4-mini',
|
|
42
|
+
label: 'gpt-5.4-mini',
|
|
43
|
+
effort: {
|
|
44
|
+
default: 'medium',
|
|
45
|
+
values: [{ value: 'low' }, { value: 'medium' }, { value: 'high' }, { value: 'xhigh' }],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
29
48
|
],
|
|
30
49
|
DEFAULT: 'gpt-5.4',
|
|
31
50
|
};
|
|
@@ -37,6 +56,11 @@ type CodexCachedModel = {
|
|
|
37
56
|
priority?: number;
|
|
38
57
|
visibility?: string;
|
|
39
58
|
supported_in_api?: boolean;
|
|
59
|
+
default_reasoning_level?: string;
|
|
60
|
+
supported_reasoning_levels?: Array<{
|
|
61
|
+
effort?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
}>;
|
|
40
64
|
};
|
|
41
65
|
|
|
42
66
|
const CODEX_MODELS_CACHE_PATH = path.join(os.homedir(), '.codex', 'models_cache.json');
|
|
@@ -51,15 +75,39 @@ const readCodexPriority = (value: unknown): number => (
|
|
|
51
75
|
typeof value === 'number' && Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER
|
|
52
76
|
);
|
|
53
77
|
|
|
54
|
-
const mapCodexModel = (model: CodexCachedModel): ProviderModelOption =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
const mapCodexModel = (model: CodexCachedModel): ProviderModelOption => {
|
|
79
|
+
const effortValues = Array.isArray(model.supported_reasoning_levels)
|
|
80
|
+
? model.supported_reasoning_levels
|
|
81
|
+
.map((level) => {
|
|
82
|
+
const value = readOptionalString(level?.effort);
|
|
83
|
+
if (!value) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
value,
|
|
89
|
+
description: readOptionalString(level?.description),
|
|
90
|
+
};
|
|
91
|
+
})
|
|
92
|
+
.filter((level): level is NonNullable<typeof level> => Boolean(level))
|
|
93
|
+
: [];
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
value: model.slug as string,
|
|
97
|
+
label: readOptionalString(model.display_name) ?? (model.slug as string),
|
|
98
|
+
description: readOptionalString(model.description),
|
|
99
|
+
effort: effortValues.length > 0
|
|
100
|
+
? {
|
|
101
|
+
default: readOptionalString(model.default_reasoning_level) ?? undefined,
|
|
102
|
+
values: effortValues,
|
|
103
|
+
}
|
|
104
|
+
: undefined,
|
|
105
|
+
};
|
|
106
|
+
};
|
|
59
107
|
|
|
60
108
|
const buildCodexModelsDefinition = (models: CodexCachedModel[]): ProviderModelsDefinition => {
|
|
61
109
|
const sortedModels = [...models]
|
|
62
|
-
.filter((model) => model.visibility
|
|
110
|
+
.filter((model) => model.visibility === 'list' && model.supported_in_api !== false)
|
|
63
111
|
.sort((left, right) => readCodexPriority(left.priority) - readCodexPriority(right.priority));
|
|
64
112
|
|
|
65
113
|
const options: ProviderModelOption[] = [];
|
|
@@ -74,6 +74,13 @@ const VERSION_TOKEN = /^[a-z]\d+$/i;
|
|
|
74
74
|
const NUMERIC_TOKEN = /^\d+(?:\.\d+)*$/;
|
|
75
75
|
const SHORT_ACRONYM_TOKEN = /^[a-z]{2,3}$/;
|
|
76
76
|
|
|
77
|
+
type OpenCodeVerboseModel = {
|
|
78
|
+
id?: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
providerID?: string;
|
|
81
|
+
variants?: Record<string, unknown>;
|
|
82
|
+
};
|
|
83
|
+
|
|
77
84
|
export const parseOpenCodeModelsStdout = (stdout: string): string[] => {
|
|
78
85
|
const ids: string[] = [];
|
|
79
86
|
|
|
@@ -91,6 +98,83 @@ export const parseOpenCodeModelsStdout = (stdout: string): string[] => {
|
|
|
91
98
|
return [...new Set(ids)];
|
|
92
99
|
};
|
|
93
100
|
|
|
101
|
+
const countJsonBraceDelta = (value: string): number => {
|
|
102
|
+
let delta = 0;
|
|
103
|
+
let inString = false;
|
|
104
|
+
let escaped = false;
|
|
105
|
+
|
|
106
|
+
for (const character of value) {
|
|
107
|
+
if (escaped) {
|
|
108
|
+
escaped = false;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (character === '\\') {
|
|
113
|
+
escaped = inString;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (character === '"') {
|
|
118
|
+
inString = !inString;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (inString) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (character === '{') {
|
|
127
|
+
delta += 1;
|
|
128
|
+
} else if (character === '}') {
|
|
129
|
+
delta -= 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return delta;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const isOpenCodeVerboseModel = (value: unknown): value is OpenCodeVerboseModel => {
|
|
137
|
+
const record = readObjectRecord(value);
|
|
138
|
+
return Boolean(record && readOptionalString(record.id));
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const parseOpenCodeVerboseModelsStdout = (stdout: string): OpenCodeVerboseModel[] => {
|
|
142
|
+
const models: OpenCodeVerboseModel[] = [];
|
|
143
|
+
let buffer: string[] = [];
|
|
144
|
+
let depth = 0;
|
|
145
|
+
|
|
146
|
+
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
147
|
+
const line = rawLine.trim();
|
|
148
|
+
if (buffer.length === 0) {
|
|
149
|
+
if (line === '{') {
|
|
150
|
+
buffer = [rawLine];
|
|
151
|
+
depth = 1;
|
|
152
|
+
}
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
buffer.push(rawLine);
|
|
157
|
+
depth += countJsonBraceDelta(rawLine);
|
|
158
|
+
|
|
159
|
+
if (depth !== 0) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(buffer.join('\n'));
|
|
165
|
+
if (isOpenCodeVerboseModel(parsed)) {
|
|
166
|
+
models.push(parsed);
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
// Ignore malformed verbose blocks and fall back to the plain id parser.
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
buffer = [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return models;
|
|
176
|
+
};
|
|
177
|
+
|
|
94
178
|
const formatDateToken = (token: string): string => (
|
|
95
179
|
`${token.slice(0, 4)}-${token.slice(4, 6)}-${token.slice(6, 8)}`
|
|
96
180
|
);
|
|
@@ -155,6 +239,20 @@ const readOpenCodeModelParts = (id: string): { upstreamProvider: string; slug: s
|
|
|
155
239
|
};
|
|
156
240
|
};
|
|
157
241
|
|
|
242
|
+
const readOpenCodeVerboseModelId = (model: OpenCodeVerboseModel): string | null => {
|
|
243
|
+
const id = readOptionalString(model.id);
|
|
244
|
+
if (!id) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (id.includes('/')) {
|
|
249
|
+
return id;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const upstreamProvider = readOptionalString(model.providerID);
|
|
253
|
+
return upstreamProvider ? `${upstreamProvider}/${id}` : id;
|
|
254
|
+
};
|
|
255
|
+
|
|
158
256
|
const labelForOpenCodeModelId = (id: string): string => {
|
|
159
257
|
const fallbackLabel = OPENCODE_FALLBACK_MODELS.OPTIONS.find((option) => option.value === id)?.label;
|
|
160
258
|
if (fallbackLabel) {
|
|
@@ -170,6 +268,52 @@ const descriptionForOpenCodeModelId = (id: string): string => {
|
|
|
170
268
|
return upstreamProvider ? `${upstreamProvider} - ${id}` : id;
|
|
171
269
|
};
|
|
172
270
|
|
|
271
|
+
const readOpenCodeVariantEffort = (key: string, value: unknown): string | null => {
|
|
272
|
+
const variant = readObjectRecord(value);
|
|
273
|
+
return readOptionalString(variant?.reasoningEffort)
|
|
274
|
+
?? readOptionalString(variant?.effort)
|
|
275
|
+
?? key;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const readOpenCodeEffortValues = (
|
|
279
|
+
variants: OpenCodeVerboseModel['variants'],
|
|
280
|
+
): NonNullable<ProviderModelOption['effort']>['values'] => {
|
|
281
|
+
const effortValues: NonNullable<ProviderModelOption['effort']>['values'] = [];
|
|
282
|
+
const seenValues = new Set<string>();
|
|
283
|
+
|
|
284
|
+
for (const [key, value] of Object.entries(variants ?? {})) {
|
|
285
|
+
const effort = readOpenCodeVariantEffort(key, value);
|
|
286
|
+
if (!effort || seenValues.has(effort)) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
seenValues.add(effort);
|
|
291
|
+
effortValues.push({ value: effort });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return effortValues;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const mapOpenCodeVerboseModel = (model: OpenCodeVerboseModel): ProviderModelOption | null => {
|
|
298
|
+
const value = readOpenCodeVerboseModelId(model);
|
|
299
|
+
if (!value) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const effortValues = readOpenCodeEffortValues(model.variants);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
value,
|
|
307
|
+
label: readOptionalString(model.name) ?? labelForOpenCodeModelId(value),
|
|
308
|
+
description: descriptionForOpenCodeModelId(value),
|
|
309
|
+
effort: effortValues.length > 0
|
|
310
|
+
? {
|
|
311
|
+
values: effortValues,
|
|
312
|
+
}
|
|
313
|
+
: undefined,
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
|
|
173
317
|
export const buildOpenCodeDefinitionFromIds = (ids: string[]): ProviderModelsDefinition => {
|
|
174
318
|
const options: ProviderModelOption[] = ids.map((value) => ({
|
|
175
319
|
value,
|
|
@@ -187,6 +331,36 @@ export const buildOpenCodeDefinitionFromIds = (ids: string[]): ProviderModelsDef
|
|
|
187
331
|
};
|
|
188
332
|
};
|
|
189
333
|
|
|
334
|
+
export const buildOpenCodeDefinitionFromVerboseModels = (
|
|
335
|
+
models: OpenCodeVerboseModel[],
|
|
336
|
+
): ProviderModelsDefinition => {
|
|
337
|
+
const options: ProviderModelOption[] = [];
|
|
338
|
+
const seenValues = new Set<string>();
|
|
339
|
+
|
|
340
|
+
for (const model of models) {
|
|
341
|
+
const mappedModel = mapOpenCodeVerboseModel(model);
|
|
342
|
+
if (!mappedModel || seenValues.has(mappedModel.value)) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
seenValues.add(mappedModel.value);
|
|
347
|
+
options.push(mappedModel);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (options.length === 0) {
|
|
351
|
+
return OPENCODE_FALLBACK_MODELS;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const defaultValue = options.find((option) => option.value === OPENCODE_FALLBACK_MODELS.DEFAULT)?.value
|
|
355
|
+
?? options[0]?.value
|
|
356
|
+
?? OPENCODE_FALLBACK_MODELS.DEFAULT;
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
OPTIONS: options,
|
|
360
|
+
DEFAULT: defaultValue,
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
|
|
190
364
|
const parseOpenCodeSessionModelValue = (rawModel: unknown): string | null => {
|
|
191
365
|
if (typeof rawModel === 'string') {
|
|
192
366
|
const trimmed = rawModel.trim();
|
|
@@ -214,7 +388,7 @@ const parseOpenCodeSessionModelValue = (rawModel: unknown): string | null => {
|
|
|
214
388
|
};
|
|
215
389
|
|
|
216
390
|
const runOpenCodeModelsCommand = (): Promise<string> => new Promise((resolve, reject) => {
|
|
217
|
-
const openCodeProcess = spawnFunction('opencode', ['models'], {
|
|
391
|
+
const openCodeProcess = spawnFunction('opencode', ['models', '--verbose'], {
|
|
218
392
|
cwd: process.cwd(),
|
|
219
393
|
env: { ...process.env },
|
|
220
394
|
});
|
|
@@ -273,6 +447,11 @@ export class OpenCodeProviderModels implements IProviderModels {
|
|
|
273
447
|
async getSupportedModels(): Promise<ProviderModelsDefinition> {
|
|
274
448
|
try {
|
|
275
449
|
const stdout = await runOpenCodeModelsCommand();
|
|
450
|
+
const verboseModels = parseOpenCodeVerboseModelsStdout(stdout);
|
|
451
|
+
if (verboseModels.length > 0) {
|
|
452
|
+
return buildOpenCodeDefinitionFromVerboseModels(verboseModels);
|
|
453
|
+
}
|
|
454
|
+
|
|
276
455
|
const ids = parseOpenCodeModelsStdout(stdout);
|
|
277
456
|
if (ids.length === 0) {
|
|
278
457
|
return OPENCODE_FALLBACK_MODELS;
|
|
@@ -21,6 +21,8 @@ type ProviderCapabilities = {
|
|
|
21
21
|
supportsPermissionRequests: boolean;
|
|
22
22
|
/** Whether the token-usage endpoint has data for this provider. */
|
|
23
23
|
supportsTokenUsage: boolean;
|
|
24
|
+
/** Whether the provider runtime can accept model-level reasoning effort. */
|
|
25
|
+
supportsEffort: boolean;
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -38,6 +40,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
38
40
|
supportsAbort: true,
|
|
39
41
|
supportsPermissionRequests: true,
|
|
40
42
|
supportsTokenUsage: true,
|
|
43
|
+
supportsEffort: true,
|
|
41
44
|
},
|
|
42
45
|
cursor: {
|
|
43
46
|
provider: 'cursor',
|
|
@@ -47,6 +50,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
47
50
|
supportsAbort: true,
|
|
48
51
|
supportsPermissionRequests: false,
|
|
49
52
|
supportsTokenUsage: false,
|
|
53
|
+
supportsEffort: false,
|
|
50
54
|
},
|
|
51
55
|
codex: {
|
|
52
56
|
provider: 'codex',
|
|
@@ -56,6 +60,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
56
60
|
supportsAbort: true,
|
|
57
61
|
supportsPermissionRequests: false,
|
|
58
62
|
supportsTokenUsage: true,
|
|
63
|
+
supportsEffort: true,
|
|
59
64
|
},
|
|
60
65
|
gemini: {
|
|
61
66
|
provider: 'gemini',
|
|
@@ -65,6 +70,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
65
70
|
supportsAbort: true,
|
|
66
71
|
supportsPermissionRequests: false,
|
|
67
72
|
supportsTokenUsage: true,
|
|
73
|
+
supportsEffort: false,
|
|
68
74
|
},
|
|
69
75
|
opencode: {
|
|
70
76
|
provider: 'opencode',
|
|
@@ -74,6 +80,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
74
80
|
supportsAbort: true,
|
|
75
81
|
supportsPermissionRequests: false,
|
|
76
82
|
supportsTokenUsage: true,
|
|
83
|
+
supportsEffort: true,
|
|
77
84
|
},
|
|
78
85
|
};
|
|
79
86
|
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
import { readProviderSessionActiveModelChange } from '@/shared/utils.js';
|
|
17
17
|
|
|
18
18
|
export const PROVIDER_MODELS_CACHE_TTL_MS = 3 * 24 * 60 * 60 * 1000;
|
|
19
|
-
const PROVIDER_MODELS_CACHE_VERSION =
|
|
19
|
+
const PROVIDER_MODELS_CACHE_VERSION = 2;
|
|
20
20
|
const UNCACHED_PROVIDERS = new Set<LLMProvider>(['claude', 'gemini']);
|
|
21
21
|
|
|
22
22
|
type ProviderModelsServiceDependencies = {
|