@oar-sh/oar 0.9.16 → 0.9.18
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/.github/extensions/web-relay/polling/polling-loop.mjs +14 -87
- package/.github/extensions/web-relay/skills/tool-activity.mjs +5 -4
- package/CHANGELOG.md +412 -0
- package/README.md +15 -13
- package/package.json +2 -1
- package/server/claude-api-worker/claude-api-backend.mjs +345 -0
- package/server/claude-api-worker/claude-api-session-worker.mjs +29 -0
- package/server/claude-worker/claude-attachments.mjs +7 -4
- package/server/claude-worker/claude-cli-pinned-env.mjs +150 -0
- package/server/claude-worker/claude-remote-relay-tool.mjs +3 -3
- package/server/claude-worker/claude-sdk-adapter.mjs +199 -15
- package/server/claude-worker/claude-session-process.mjs +276 -33
- package/server/claude-worker/claude-session-worker-main.mjs +269 -0
- package/server/claude-worker/claude-session-worker.mjs +2 -253
- package/server/claude-worker/claude-subscription-key-check.mjs +64 -0
- package/server/claude-worker/claude-turn-failure.mjs +8 -0
- package/server/claude-worker/claude-turn-publisher.mjs +394 -13
- package/server/claude-worker/claude-usage-totals.mjs +161 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +56 -1
- package/server/copilot-worker/copilot-remote-relay-tool.mjs +2 -2
- package/server/copilot-worker/copilot-sdk-adapter.mjs +18 -4
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +1 -1
- package/server/copilot-worker/copilot-sdk-session-process.mjs +80 -14
- package/server/cursor-worker/cursor-remote-relay-tool.mjs +3 -3
- package/server/cursor-worker/cursor-turn-runner.mjs +4 -4
- package/server/cursor-worker/sdk-message-normalizer.mjs +2 -2
- package/server/db-schema.mjs +96 -1
- package/server/deepseek-worker/deepseek-claude-backend.mjs +317 -0
- package/server/deepseek-worker/deepseek-session-worker.mjs +37 -0
- package/server/grok-worker/acp-client.mjs +1 -1
- package/server/grok-worker/grok-turn-runner.mjs +4 -4
- package/server/grok-worker/sdk-message-normalizer.mjs +1 -1
- package/server/mcp/oar-mcp-server.mjs +11 -5
- package/server/migrations/0024-deepseek-usage-totals.mjs +151 -0
- package/server/migrations/0025-deepseek-usage-requests-daily.mjs +179 -0
- package/server/migrations/0026-deepseek-usage-served-models.mjs +222 -0
- package/server/migrations/0027-deepseek-usage-checkpoint-runs.mjs +101 -0
- package/server/migrations/0028-deepseek-usage-checkpoint-request-runs.mjs +99 -0
- package/server/migrations/0029-deepseek-balance-rises.mjs +99 -0
- package/server/migrations/0030-deepseek-usage-actuals.mjs +123 -0
- package/server/migrations/0031-queue-provider.mjs +143 -0
- package/server/migrations/0032-runtime-session-billing-switch.mjs +94 -0
- package/server/migrations/0033-claude-api-usage-checkpoints.mjs +138 -0
- package/server/migrations/0034-claude-api-usage-totals.mjs +112 -0
- package/server/public/app/activity-replay-state.mjs +4 -3
- package/server/public/app/agent-sessions-settings-ui.js +1 -1
- package/server/public/app/agent-sessions-settings.mjs +1 -1
- package/server/public/app/api-client.js +105 -0
- package/server/public/app/bootstrap.js +243 -29
- package/server/public/app/claude-api-settings-ui.js +362 -0
- package/server/public/app/claude-billing-toggle-ui.js +224 -0
- package/server/public/app/claude-billing-toggle.mjs +112 -0
- package/server/public/app/context-tier-options.mjs +10 -2
- package/server/public/app/context-usage-view.mjs +1 -1
- package/server/public/app/conversation-provider-indicator.mjs +18 -0
- package/server/public/app/conversation-view.js +31 -3
- package/server/public/app/deepseek-settings-ui.js +342 -0
- package/server/public/app/inbox-ui.js +14 -3
- package/server/public/app/inbox-view.mjs +1 -1
- package/server/public/app/journal-view.js +156 -11
- package/server/public/app/model-selector-options.mjs +28 -1
- package/server/public/app/model-vision-support.mjs +2 -0
- package/server/public/app/new-conversation-model-choice.mjs +21 -0
- package/server/public/app/no-conversation-state.mjs +1 -1
- package/server/public/app/plan-usage-view.mjs +165 -13
- package/server/public/app/provider-readiness.mjs +8 -2
- package/server/public/app/relay-error-ctas.mjs +58 -0
- package/server/public/app/relay-schedules-ui.js +1 -1
- package/server/public/app/settings-modal.js +22 -0
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +28 -1
- package/server/public/app/tool-activity-emoji.mjs +4 -3
- package/server/public/app/usage-limit-ui.js +136 -5
- package/server/public/index.html +596 -35
- package/server/relay-tools.md +4 -4
- package/server/repositories/conversation-group-repository.mjs +14 -7
- package/server/repositories/message-repository.mjs +7 -0
- package/server/repositories/session-repository.mjs +8 -1
- package/server/routes/ask-user-routes.mjs +1 -1
- package/server/routes/conversation-group-routes.mjs +3 -2
- package/server/routes/deepseek-usage-import-routes.mjs +72 -0
- package/server/routes/messages-routes.mjs +831 -36
- package/server/routes/relay-ask-routes.mjs +3 -3
- package/server/routes/relay-event-routes.mjs +2 -2
- package/server/routes/relay-schedule-routes.mjs +1 -1
- package/server/routes/remote-relay-routes.mjs +3 -3
- package/server/routes/sessions-routes.mjs +670 -63
- package/server/server-runtime.mjs +476 -26
- package/server/services/about-service.mjs +2 -0
- package/server/services/background-work-view.mjs +2 -1
- package/server/services/claude-api-settings-service.mjs +575 -0
- package/server/services/claude-api-usage-service.mjs +361 -0
- package/server/services/claude-billing-swap-service.mjs +274 -0
- package/server/services/claude-billing-switch-service.mjs +343 -0
- package/server/services/claude-session-leftovers.mjs +226 -0
- package/server/services/deepseek-balance-service.mjs +102 -0
- package/server/services/deepseek-settings-service.mjs +380 -0
- package/server/services/deepseek-usage-import.mjs +471 -0
- package/server/services/model-variant-catalog-service.mjs +1 -0
- package/server/services/plan-usage-claude-api.mjs +445 -0
- package/server/services/plan-usage-contract.mjs +100 -5
- package/server/services/plan-usage-deepseek.mjs +931 -0
- package/server/services/plan-usage-service.mjs +436 -0
- package/server/services/provider-model-selection.mjs +4 -2
- package/server/services/provider-reasoning-effort.mjs +24 -2
- package/server/services/relay-ask-service.mjs +1 -1
- package/server/services/relay-event-service.mjs +3 -2
- package/server/services/relay-schedule-service.mjs +1 -1
- package/server/services/remote-relay-caller-context.mjs +7 -0
- package/server/services/remote-relay-dispatcher.mjs +84 -14
- package/server/services/remote-relay-registry-service.mjs +1 -1
- package/server/services/session-worker-launch-service.mjs +294 -84
- package/server/services/session-worker-registry-service.mjs +28 -1
- package/server/services/session-worker-websocket-service.mjs +17 -0
- package/server/services/usage-limit-pause-service.mjs +55 -4
- package/server/services/worker-secret-redaction.mjs +320 -0
- package/shared/anthropic-api.mjs +202 -0
- package/shared/context-window-fallbacks.mjs +3 -0
- package/shared/deepseek-api.mjs +137 -0
- package/shared/deepseek-models.mjs +114 -0
- package/shared/deepseek-pricing.mjs +146 -0
- package/shared/host-claude-session-env.mjs +101 -0
- package/shared/model-id.mjs +7 -0
- package/shared/preview-tool-core.mjs +1 -1
- package/shared/provider-routing.mjs +5 -3
- package/shared/relay-asks.mjs +11 -5
- package/shared/relay-events.mjs +2 -2
- package/shared/relay-schedules.mjs +2 -2
- package/shared/remote-relay-contract.mjs +29 -9
- package/shared/remote-relay-tool-core.mjs +23 -6
- package/shared/steer-settle-failure.mjs +2 -0
- package/shared/worker-runtime/api-client.mjs +2 -2
- package/shared/worker-runtime/direct-openai-image.mjs +128 -0
- package/shared/worker-runtime/heartbeat.mjs +5 -0
- package/shared/worker-runtime/worker-log-file.mjs +60 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +6 -0
|
@@ -79,6 +79,8 @@ import {
|
|
|
79
79
|
updateClaudeSettings,
|
|
80
80
|
updateCursorSettings,
|
|
81
81
|
updateGrokSettings,
|
|
82
|
+
updateDeepSeekSettings,
|
|
83
|
+
updateClaudeApiSettings,
|
|
82
84
|
loadConversation,
|
|
83
85
|
refreshConversationHistory,
|
|
84
86
|
updateConversationTitle,
|
|
@@ -180,6 +182,7 @@ import {
|
|
|
180
182
|
composerPlaceholderFor,
|
|
181
183
|
contextWindowSuffix,
|
|
182
184
|
humanizeModelLabel,
|
|
185
|
+
distinctModelLabels,
|
|
183
186
|
modelMetadataFor,
|
|
184
187
|
modelSelectorOptionsEqual,
|
|
185
188
|
splitModelVariantId,
|
|
@@ -323,6 +326,19 @@ import {
|
|
|
323
326
|
toggleGrokProvider,
|
|
324
327
|
applyGrokSettingsState,
|
|
325
328
|
refreshGrokSettingsState,
|
|
329
|
+
saveDeepSeekSettings,
|
|
330
|
+
removeDeepSeekSettings,
|
|
331
|
+
toggleDeepSeekProvider,
|
|
332
|
+
importDeepSeekUsageExport,
|
|
333
|
+
applyDeepSeekSettingsState,
|
|
334
|
+
refreshDeepSeekSettingsState,
|
|
335
|
+
saveClaudeApiSettings,
|
|
336
|
+
removeClaudeApiSettings,
|
|
337
|
+
toggleClaudeApiProvider,
|
|
338
|
+
refreshClaudeApiModels,
|
|
339
|
+
openClaudeTabFromClaudeApi,
|
|
340
|
+
applyClaudeApiSettingsState,
|
|
341
|
+
refreshClaudeApiSettingsState,
|
|
326
342
|
saveCursorSettings,
|
|
327
343
|
removeCursorSettings,
|
|
328
344
|
saveCursorAllowanceSettings,
|
|
@@ -366,6 +382,7 @@ import {
|
|
|
366
382
|
} from './action-confirmations.js';
|
|
367
383
|
import { applyPendingActionsFromStatus, initHostSuspendUi } from './host-suspend-ui.js';
|
|
368
384
|
import { applyUsageLimitFromStatus, initUsageLimitUi } from './usage-limit-ui.js';
|
|
385
|
+
import { initClaudeBillingToggle, renderClaudeBillingToggle } from './claude-billing-toggle-ui.js';
|
|
369
386
|
import {
|
|
370
387
|
startClaudeRelogin,
|
|
371
388
|
submitClaudeLoginCodeFromInput,
|
|
@@ -442,9 +459,11 @@ const PROVIDER_LABELS = {
|
|
|
442
459
|
openai: 'OpenAI',
|
|
443
460
|
'openai-byok': 'OpenAI (BYOK)',
|
|
444
461
|
claude: 'Claude SDK',
|
|
462
|
+
'claude-api': 'Claude API',
|
|
445
463
|
'claude-cloud': 'Claude Cloud',
|
|
446
464
|
cursor: 'Cursor SDK',
|
|
447
465
|
grok: 'Grok',
|
|
466
|
+
deepseek: 'DeepSeek',
|
|
448
467
|
'github-copilot': 'GitHub Copilot',
|
|
449
468
|
// Vendor grouping for Copilot-served rows, distinct from the Claude SDK runtime.
|
|
450
469
|
anthropic: 'Anthropic',
|
|
@@ -514,6 +533,7 @@ let modelCatalogState = {
|
|
|
514
533
|
reasoningEfforts: [],
|
|
515
534
|
modelMetadataByModel: {},
|
|
516
535
|
claudeContextTiersByModel: {},
|
|
536
|
+
claudeApiContextTiersByModel: {},
|
|
517
537
|
stale: true,
|
|
518
538
|
metadataValid: false,
|
|
519
539
|
reasoningMetadataValid: false,
|
|
@@ -1436,6 +1456,8 @@ function syncSessionLockNote({ pinnedModel = '' } = {}) {
|
|
|
1436
1456
|
} else {
|
|
1437
1457
|
delete note.dataset.provider;
|
|
1438
1458
|
}
|
|
1459
|
+
// The billing toggle sits under the note and follows the same chat.
|
|
1460
|
+
renderClaudeBillingToggle();
|
|
1439
1461
|
}
|
|
1440
1462
|
|
|
1441
1463
|
function applyModelMetadataHardFail(message = '') {
|
|
@@ -1502,9 +1524,11 @@ function normalizeModelSelectorProviderType(providerType = '') {
|
|
|
1502
1524
|
const normalized = String(providerType || '').trim().toLowerCase();
|
|
1503
1525
|
if (normalized === 'openai' || normalized === 'openai-byok') return 'openai';
|
|
1504
1526
|
if (normalized === 'claude') return 'claude';
|
|
1527
|
+
if (normalized === 'claude-api') return 'claude-api';
|
|
1505
1528
|
if (normalized === CLAUDE_CLOUD_PROVIDER) return CLAUDE_CLOUD_PROVIDER;
|
|
1506
1529
|
if (normalized === 'cursor') return 'cursor';
|
|
1507
1530
|
if (normalized === 'grok') return 'grok';
|
|
1531
|
+
if (normalized === 'deepseek') return 'deepseek';
|
|
1508
1532
|
return 'github';
|
|
1509
1533
|
}
|
|
1510
1534
|
|
|
@@ -1532,6 +1556,10 @@ function modelVisibleForActiveProvider(modelId, activeProviderType, providersByM
|
|
|
1532
1556
|
const hasClaude = providers.includes('claude');
|
|
1533
1557
|
const hasCursor = providers.includes('cursor');
|
|
1534
1558
|
const hasGrok = providers.includes('grok');
|
|
1559
|
+
const hasDeepSeek = providers.includes('deepseek');
|
|
1560
|
+
// Claude API rows are the Claude provider's ids on an API key: as
|
|
1561
|
+
// exclusive as the other runtimes' rows.
|
|
1562
|
+
const hasClaudeApi = providers.includes('claude-api');
|
|
1535
1563
|
// Only present if a relay tags catalog rows for Claude Cloud; such a row is
|
|
1536
1564
|
// then as exclusive as the other runtimes' rows.
|
|
1537
1565
|
const hasClaudeCloud = providers.includes(CLAUDE_CLOUD_PROVIDER);
|
|
@@ -1540,14 +1568,18 @@ function modelVisibleForActiveProvider(modelId, activeProviderType, providersByM
|
|
|
1540
1568
|
&& provider !== 'claude'
|
|
1541
1569
|
&& provider !== 'cursor'
|
|
1542
1570
|
&& provider !== 'grok'
|
|
1571
|
+
&& provider !== 'deepseek'
|
|
1572
|
+
&& provider !== 'claude-api'
|
|
1543
1573
|
&& provider !== CLAUDE_CLOUD_PROVIDER
|
|
1544
1574
|
));
|
|
1545
1575
|
const activeProvider = normalizeModelSelectorProviderType(activeProviderType);
|
|
1546
1576
|
if (activeProvider === 'openai') return hasOpenAIByok;
|
|
1547
1577
|
if (activeProvider === 'claude') return hasClaude;
|
|
1578
|
+
if (activeProvider === 'claude-api') return hasClaudeApi;
|
|
1548
1579
|
if (activeProvider === 'cursor') return hasCursor;
|
|
1549
1580
|
if (activeProvider === 'grok') return hasGrok;
|
|
1550
|
-
|
|
1581
|
+
if (activeProvider === 'deepseek') return hasDeepSeek;
|
|
1582
|
+
return !((hasOpenAIByok || hasClaude || hasCursor || hasGrok || hasDeepSeek || hasClaudeApi || hasClaudeCloud) && !hasNonExclusiveProvider);
|
|
1551
1583
|
}
|
|
1552
1584
|
|
|
1553
1585
|
// A Claude Cloud conversation does not pick from the shared catalog: its
|
|
@@ -1583,10 +1615,16 @@ function buildModelSelectorOptions(
|
|
|
1583
1615
|
if (normalizeModelSelectorProviderType(activeProviderType) === CLAUDE_CLOUD_PROVIDER) {
|
|
1584
1616
|
return buildClaudeCloudModelSelectorOptions(models, providersByModel, metadataByModel);
|
|
1585
1617
|
}
|
|
1586
|
-
|
|
1618
|
+
// Only this provider's models are labelled: two ids that read the same
|
|
1619
|
+
// fall back to their raw ids, and an id another provider serves
|
|
1620
|
+
// ("claude-haiku-4-5" beside the key's "claude-haiku-4-5-20251001") must
|
|
1621
|
+
// not do that to a list it is not in.
|
|
1622
|
+
const visibleModels = (models.length ? models : [FALLBACK_MODEL])
|
|
1623
|
+
.filter((modelId) => modelVisibleForActiveProvider(modelId, activeProviderType, providersByModel));
|
|
1624
|
+
const normalizedOptions = buildCatalogModelOptions(visibleModels, {
|
|
1587
1625
|
autoValue: AUTO_MODEL_OPTION,
|
|
1588
1626
|
metadataByModel,
|
|
1589
|
-
annotateContextWindow: normalizeModelSelectorProviderType(activeProviderType)
|
|
1627
|
+
annotateContextWindow: !['claude', 'claude-api'].includes(normalizeModelSelectorProviderType(activeProviderType)),
|
|
1590
1628
|
});
|
|
1591
1629
|
return normalizedOptions.filter((option) => modelVisibleForActiveProvider(
|
|
1592
1630
|
option.value,
|
|
@@ -1792,6 +1830,12 @@ function updateModelCatalogState(payload) {
|
|
|
1792
1830
|
Array.isArray(tiers) ? tiers : [],
|
|
1793
1831
|
]))
|
|
1794
1832
|
: {},
|
|
1833
|
+
claudeApiContextTiersByModel: payload?.claudeApiContextTiersByModel && typeof payload.claudeApiContextTiersByModel === 'object'
|
|
1834
|
+
? Object.fromEntries(Object.entries(payload.claudeApiContextTiersByModel).map(([modelId, tiers]) => [
|
|
1835
|
+
String(modelId || '').trim().toLowerCase(),
|
|
1836
|
+
Array.isArray(tiers) ? tiers : [],
|
|
1837
|
+
]))
|
|
1838
|
+
: {},
|
|
1795
1839
|
stale: !!payload?.stale,
|
|
1796
1840
|
metadataValid: payload?.metadataValid === true,
|
|
1797
1841
|
reasoningMetadataValid: payload?.reasoningMetadataValid === true,
|
|
@@ -1923,7 +1967,9 @@ function updateContextTierSelector(modelId) {
|
|
|
1923
1967
|
modelId,
|
|
1924
1968
|
providerType: activeComposerProviderType(),
|
|
1925
1969
|
metadata: modelMetadataFor(modelId, modelCatalogState.modelMetadataByModel) || {},
|
|
1926
|
-
claudeTiers:
|
|
1970
|
+
claudeTiers: activeComposerProviderType() === 'claude-api'
|
|
1971
|
+
? modelCatalogState.claudeApiContextTiersByModel
|
|
1972
|
+
: modelCatalogState.claudeContextTiersByModel,
|
|
1927
1973
|
});
|
|
1928
1974
|
const current = select.value;
|
|
1929
1975
|
select.innerHTML = '';
|
|
@@ -1945,8 +1991,9 @@ function updateModelPricingDetails(modelId, tier) {
|
|
|
1945
1991
|
if (!details || !summary || !grid) return;
|
|
1946
1992
|
const metadata = modelCatalogState.modelMetadataByModel?.[modelId] || {};
|
|
1947
1993
|
// The catalog's prices are Copilot credits; a cloud session is billed to the
|
|
1948
|
-
// Claude account
|
|
1949
|
-
|
|
1994
|
+
// Claude account and a Claude API chat to the Console in dollars, so they
|
|
1995
|
+
// would be the wrong numbers for them.
|
|
1996
|
+
const pricing = activeComposerProviderType() === CLAUDE_CLOUD_PROVIDER || activeComposerProviderType() === 'claude-api'
|
|
1950
1997
|
? null
|
|
1951
1998
|
: metadata?.pricing?.[tier === 'long_context' ? 'longContext' : 'default'];
|
|
1952
1999
|
if (!pricing || typeof pricing !== 'object') {
|
|
@@ -1988,8 +2035,10 @@ const RELAY_MODES_BY_PROVIDER = {
|
|
|
1988
2035
|
github: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1989
2036
|
openai: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1990
2037
|
claude: ['agent', 'ask', 'plan', 'autopilot'],
|
|
2038
|
+
'claude-api': ['agent', 'ask', 'plan', 'autopilot'],
|
|
1991
2039
|
cursor: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1992
2040
|
grok: ['agent', 'ask', 'plan', 'autopilot'],
|
|
2041
|
+
deepseek: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1993
2042
|
'claude-cloud': ['agent'],
|
|
1994
2043
|
};
|
|
1995
2044
|
|
|
@@ -2053,7 +2102,7 @@ async function persistCurrentConversationPreferences() {
|
|
|
2053
2102
|
const contextTier = String(document.getElementById('context-tier-select')?.value || 'default').trim().toLowerCase();
|
|
2054
2103
|
const preferredModelWithTier = model
|
|
2055
2104
|
&& contextTier === 'long_context'
|
|
2056
|
-
&& activeComposerProviderType() === 'claude'
|
|
2105
|
+
&& (activeComposerProviderType() === 'claude' || activeComposerProviderType() === 'claude-api')
|
|
2057
2106
|
&& !CLAUDE_LONG_CONTEXT_PATTERN.test(model)
|
|
2058
2107
|
? `${model}[1m]`
|
|
2059
2108
|
: model;
|
|
@@ -2336,6 +2385,20 @@ function reportOpenAIModelDiscoveryFailure(payload) {
|
|
|
2336
2385
|
8000,
|
|
2337
2386
|
);
|
|
2338
2387
|
}
|
|
2388
|
+
const deepseekDiscovery = payload?.deepseekModelDiscovery;
|
|
2389
|
+
if (deepseekDiscovery && deepseekDiscovery.ok === false && !deepseekDiscovery.skipped) {
|
|
2390
|
+
showTransientRelayNotice(
|
|
2391
|
+
`DeepSeek model discovery failed. Cached DeepSeek models were kept: ${deepseekDiscovery.error || 'unknown error'}`,
|
|
2392
|
+
8000,
|
|
2393
|
+
);
|
|
2394
|
+
}
|
|
2395
|
+
const claudeApiDiscovery = payload?.claudeApiModelDiscovery;
|
|
2396
|
+
if (claudeApiDiscovery && claudeApiDiscovery.ok === false && !claudeApiDiscovery.skipped) {
|
|
2397
|
+
showTransientRelayNotice(
|
|
2398
|
+
`Claude API model discovery failed. Cached Claude API models were kept: ${claudeApiDiscovery.error || 'unknown error'}`,
|
|
2399
|
+
8000,
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2339
2402
|
}
|
|
2340
2403
|
|
|
2341
2404
|
async function retryModelMetadataRefresh() {
|
|
@@ -2473,6 +2536,28 @@ function applyModelVariantCatalogState(payload) {
|
|
|
2473
2536
|
: [],
|
|
2474
2537
|
}
|
|
2475
2538
|
: null,
|
|
2539
|
+
deepseekModels: payload?.deepseekModels && typeof payload.deepseekModels === 'object'
|
|
2540
|
+
? {
|
|
2541
|
+
defaultModel: String(payload.deepseekModels.defaultModel || '').trim(),
|
|
2542
|
+
availableModels: Array.isArray(payload.deepseekModels.availableModels)
|
|
2543
|
+
? payload.deepseekModels.availableModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2544
|
+
: [],
|
|
2545
|
+
enabledModels: Array.isArray(payload.deepseekModels.enabledModels)
|
|
2546
|
+
? payload.deepseekModels.enabledModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2547
|
+
: [],
|
|
2548
|
+
}
|
|
2549
|
+
: null,
|
|
2550
|
+
claudeApiModels: payload?.claudeApiModels && typeof payload.claudeApiModels === 'object'
|
|
2551
|
+
? {
|
|
2552
|
+
defaultModel: String(payload.claudeApiModels.defaultModel || '').trim(),
|
|
2553
|
+
availableModels: Array.isArray(payload.claudeApiModels.availableModels)
|
|
2554
|
+
? payload.claudeApiModels.availableModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2555
|
+
: [],
|
|
2556
|
+
enabledModels: Array.isArray(payload.claudeApiModels.enabledModels)
|
|
2557
|
+
? payload.claudeApiModels.enabledModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2558
|
+
: [],
|
|
2559
|
+
}
|
|
2560
|
+
: null,
|
|
2476
2561
|
};
|
|
2477
2562
|
}
|
|
2478
2563
|
|
|
@@ -2541,6 +2626,11 @@ function renderModelVariantCatalogBody() {
|
|
|
2541
2626
|
return entryProvider === 'claude';
|
|
2542
2627
|
}
|
|
2543
2628
|
if (entryProvider === 'claude') return false;
|
|
2629
|
+
// Claude API lists the same Claude ids as the Claude SDK tab, its own rows.
|
|
2630
|
+
if (activeTab === 'claude-api') {
|
|
2631
|
+
return entryProvider === 'claude-api';
|
|
2632
|
+
}
|
|
2633
|
+
if (entryProvider === 'claude-api') return false;
|
|
2544
2634
|
if (activeTab === 'cursor') {
|
|
2545
2635
|
return entryProvider === 'cursor';
|
|
2546
2636
|
}
|
|
@@ -2549,6 +2639,10 @@ function renderModelVariantCatalogBody() {
|
|
|
2549
2639
|
return entryProvider === 'grok';
|
|
2550
2640
|
}
|
|
2551
2641
|
if (entryProvider === 'grok') return false;
|
|
2642
|
+
if (activeTab === 'deepseek') {
|
|
2643
|
+
return entryProvider === 'deepseek';
|
|
2644
|
+
}
|
|
2645
|
+
if (entryProvider === 'deepseek') return false;
|
|
2552
2646
|
if (activeTab === 'openai') {
|
|
2553
2647
|
return hasOpenAIByok || entryProvider === 'openai-byok';
|
|
2554
2648
|
}
|
|
@@ -2560,6 +2654,8 @@ function renderModelVariantCatalogBody() {
|
|
|
2560
2654
|
&& provider !== 'claude'
|
|
2561
2655
|
&& provider !== 'cursor'
|
|
2562
2656
|
&& provider !== 'grok'
|
|
2657
|
+
&& provider !== 'deepseek'
|
|
2658
|
+
&& provider !== 'claude-api'
|
|
2563
2659
|
&& provider !== CLAUDE_CLOUD_PROVIDER
|
|
2564
2660
|
));
|
|
2565
2661
|
}
|
|
@@ -2670,6 +2766,54 @@ function renderModelVariantCatalogBody() {
|
|
|
2670
2766
|
}
|
|
2671
2767
|
grouped.set('grok', providerRows);
|
|
2672
2768
|
}
|
|
2769
|
+
const deepseekCatalog = modelVariantCatalogState.deepseekModels;
|
|
2770
|
+
if (deepseekCatalog?.availableModels?.length) {
|
|
2771
|
+
const deepseekEnabledSet = new Set(deepseekCatalog.enabledModels || []);
|
|
2772
|
+
const deepseekDefaultModel = String(deepseekCatalog.defaultModel || '').trim();
|
|
2773
|
+
const providerRows = [];
|
|
2774
|
+
for (const [index, deepseekModelId] of deepseekCatalog.availableModels.entries()) {
|
|
2775
|
+
const isDefaultModel = deepseekModelId === deepseekDefaultModel;
|
|
2776
|
+
providerRows.push({
|
|
2777
|
+
variantId: `${deepseekModelId}--provider-deepseek`,
|
|
2778
|
+
baseModelId: deepseekModelId,
|
|
2779
|
+
provider: 'deepseek',
|
|
2780
|
+
label: humanizeModelLabel(deepseekModelId),
|
|
2781
|
+
releaseStatus: null,
|
|
2782
|
+
reasoningEffort: null,
|
|
2783
|
+
// The default model always stays enabled, as for the other providers.
|
|
2784
|
+
selectable: !isDefaultModel,
|
|
2785
|
+
enabled: deepseekEnabledSet.has(deepseekModelId) || isDefaultModel,
|
|
2786
|
+
deepseekModelId,
|
|
2787
|
+
sortOrder: index,
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
grouped.set('deepseek', providerRows);
|
|
2791
|
+
}
|
|
2792
|
+
const claudeApiCatalog = modelVariantCatalogState.claudeApiModels;
|
|
2793
|
+
if (claudeApiCatalog?.availableModels?.length) {
|
|
2794
|
+
const claudeApiEnabledSet = new Set(claudeApiCatalog.enabledModels || []);
|
|
2795
|
+
const claudeApiDefaultModel = String(claudeApiCatalog.defaultModel || '').trim();
|
|
2796
|
+
// Named by model; two snapshots that would read the same keep their ids.
|
|
2797
|
+
const claudeApiLabels = distinctModelLabels(claudeApiCatalog.availableModels);
|
|
2798
|
+
const providerRows = [];
|
|
2799
|
+
for (const [index, claudeApiModelId] of claudeApiCatalog.availableModels.entries()) {
|
|
2800
|
+
const isDefaultModel = claudeApiModelId === claudeApiDefaultModel;
|
|
2801
|
+
providerRows.push({
|
|
2802
|
+
variantId: `${claudeApiModelId}--provider-claude-api`,
|
|
2803
|
+
baseModelId: claudeApiModelId,
|
|
2804
|
+
provider: 'claude-api',
|
|
2805
|
+
label: claudeApiLabels.get(claudeApiModelId) || claudeApiModelId,
|
|
2806
|
+
releaseStatus: null,
|
|
2807
|
+
reasoningEffort: null,
|
|
2808
|
+
// The default model always stays enabled, as for the other providers.
|
|
2809
|
+
selectable: !isDefaultModel,
|
|
2810
|
+
enabled: claudeApiEnabledSet.has(claudeApiModelId) || isDefaultModel,
|
|
2811
|
+
claudeApiModelId,
|
|
2812
|
+
sortOrder: index,
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
grouped.set('claude-api', providerRows);
|
|
2816
|
+
}
|
|
2673
2817
|
const selected = new Set(modelVariantCatalogState.enabledVariantIds);
|
|
2674
2818
|
const selectedOrder = new Map(
|
|
2675
2819
|
modelVariantCatalogState.enabledVariantIds.map((variantId, index) => [variantId, index]),
|
|
@@ -2705,24 +2849,32 @@ function renderModelVariantCatalogBody() {
|
|
|
2705
2849
|
const hasAnthropicTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'anthropic'));
|
|
2706
2850
|
const hasCursorTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'cursor'));
|
|
2707
2851
|
const hasGrokTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'grok'));
|
|
2852
|
+
const hasDeepSeekTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'deepseek'));
|
|
2853
|
+
const hasClaudeApiTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'claude-api'));
|
|
2708
2854
|
if (modelVariantCatalogProviderTab === 'openai' && !hasOpenAITab) modelVariantCatalogProviderTab = 'copilot';
|
|
2709
2855
|
if (modelVariantCatalogProviderTab === 'anthropic' && !hasAnthropicTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2710
2856
|
if (modelVariantCatalogProviderTab === 'cursor' && !hasCursorTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2711
2857
|
if (modelVariantCatalogProviderTab === 'grok' && !hasGrokTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2858
|
+
if (modelVariantCatalogProviderTab === 'deepseek' && !hasDeepSeekTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2859
|
+
if (modelVariantCatalogProviderTab === 'claude-api' && !hasClaudeApiTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2712
2860
|
if (modelVariantCatalogProviderTab === 'copilot' && !hasCopilotTab) {
|
|
2713
2861
|
if (hasOpenAITab) modelVariantCatalogProviderTab = 'openai';
|
|
2714
2862
|
else if (hasAnthropicTab) modelVariantCatalogProviderTab = 'anthropic';
|
|
2715
2863
|
else if (hasCursorTab) modelVariantCatalogProviderTab = 'cursor';
|
|
2716
2864
|
else if (hasGrokTab) modelVariantCatalogProviderTab = 'grok';
|
|
2865
|
+
else if (hasDeepSeekTab) modelVariantCatalogProviderTab = 'deepseek';
|
|
2866
|
+
else if (hasClaudeApiTab) modelVariantCatalogProviderTab = 'claude-api';
|
|
2717
2867
|
}
|
|
2718
2868
|
const visibleProviderOrder = providerOrder.filter((providerKey) => providerBelongsToTab(providerKey, modelVariantCatalogProviderTab));
|
|
2719
2869
|
const providerTabButtons = `
|
|
2720
|
-
<div class="summary-tab-strip" style="display:flex;gap:8px;align-items:center;">
|
|
2870
|
+
<div class="summary-tab-strip" style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;">
|
|
2721
2871
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'copilot' ? ' active' : ''}" data-model-provider-tab="copilot">Copilot</button>
|
|
2722
2872
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'openai' ? ' active' : ''}" data-model-provider-tab="openai" ${hasOpenAITab ? '' : 'disabled'}>OpenAI</button>
|
|
2723
2873
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'anthropic' ? ' active' : ''}" data-model-provider-tab="anthropic" ${hasAnthropicTab ? '' : 'disabled'}>Claude SDK</button>
|
|
2874
|
+
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'claude-api' ? ' active' : ''}" data-model-provider-tab="claude-api" ${hasClaudeApiTab ? '' : 'disabled'}>Claude API</button>
|
|
2724
2875
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'cursor' ? ' active' : ''}" data-model-provider-tab="cursor" ${hasCursorTab ? '' : 'disabled'}>Cursor SDK</button>
|
|
2725
2876
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'grok' ? ' active' : ''}" data-model-provider-tab="grok" ${hasGrokTab ? '' : 'disabled'}>Grok</button>
|
|
2877
|
+
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'deepseek' ? ' active' : ''}" data-model-provider-tab="deepseek" ${hasDeepSeekTab ? '' : 'disabled'}>DeepSeek</button>
|
|
2726
2878
|
</div>
|
|
2727
2879
|
`;
|
|
2728
2880
|
const warnings = [
|
|
@@ -2775,9 +2927,10 @@ function renderModelVariantCatalogBody() {
|
|
|
2775
2927
|
: '';
|
|
2776
2928
|
const variantRowsHtml = variantRows.map((row) => {
|
|
2777
2929
|
const selectable = row.selectable !== false;
|
|
2778
|
-
// SDK-provider rows (Claude/Cursor/Grok) carry their own enabled
|
|
2779
|
-
// Copilot variant rows read the enabledVariantIds selection.
|
|
2780
|
-
const sdkProviderModelId = row.claudeModelId || row.cursorModelId || row.grokModelId ||
|
|
2930
|
+
// SDK-provider rows (Claude/Cursor/Grok/DeepSeek) carry their own enabled
|
|
2931
|
+
// flag; only Copilot variant rows read the enabledVariantIds selection.
|
|
2932
|
+
const sdkProviderModelId = row.claudeModelId || row.cursorModelId || row.grokModelId || row.deepseekModelId
|
|
2933
|
+
|| row.claudeApiModelId || '';
|
|
2781
2934
|
const checked = sdkProviderModelId
|
|
2782
2935
|
? row.enabled === true
|
|
2783
2936
|
: (selectable && selected.has(row.variantId));
|
|
@@ -2797,12 +2950,16 @@ function renderModelVariantCatalogBody() {
|
|
|
2797
2950
|
? ` data-claude-model="${escHtml(row.claudeModelId)}"`
|
|
2798
2951
|
: (row.cursorModelId
|
|
2799
2952
|
? ` data-cursor-model="${escHtml(row.cursorModelId)}"`
|
|
2800
|
-
: (row.grokModelId
|
|
2953
|
+
: (row.grokModelId
|
|
2954
|
+
? ` data-grok-model="${escHtml(row.grokModelId)}"`
|
|
2955
|
+
: (row.deepseekModelId
|
|
2956
|
+
? ` data-deepseek-model="${escHtml(row.deepseekModelId)}"`
|
|
2957
|
+
: (row.claudeApiModelId ? ` data-claude-api-model="${escHtml(row.claudeApiModelId)}"` : ''))));
|
|
2801
2958
|
return `
|
|
2802
2959
|
<label class="model-variant-row">
|
|
2803
2960
|
<input class="model-variant-checkbox" type="checkbox" data-selectable="${selectable ? '1' : '0'}" data-variant-id="${escHtml(row.variantId)}"${sdkModelAttr} ${checked ? 'checked' : ''} ${selectable ? '' : 'disabled'}>
|
|
2804
2961
|
<span class="model-variant-row-copy">
|
|
2805
|
-
<span class="model-variant-row-title">${escHtml(row.variantId)}${effortChip}${statusChip}${fixedChip}</span>
|
|
2962
|
+
<span class="model-variant-row-title"${row.claudeApiModelId ? ` title="${escHtml(row.claudeApiModelId)}"` : ''}>${escHtml(row.claudeApiModelId ? row.label : row.variantId)}${effortChip}${statusChip}${fixedChip}</span>
|
|
2806
2963
|
</span>
|
|
2807
2964
|
</label>
|
|
2808
2965
|
`;
|
|
@@ -2923,12 +3080,22 @@ async function saveSelectedModelsFromModal() {
|
|
|
2923
3080
|
.filter((input) => input.checked)
|
|
2924
3081
|
.map((input) => String(input.getAttribute('data-grok-model') || '').trim())
|
|
2925
3082
|
.filter(Boolean);
|
|
3083
|
+
const deepseekCheckboxes = Array.from(body.querySelectorAll('.model-variant-checkbox[data-deepseek-model]'));
|
|
3084
|
+
const selectedDeepSeekModels = deepseekCheckboxes
|
|
3085
|
+
.filter((input) => input.checked)
|
|
3086
|
+
.map((input) => String(input.getAttribute('data-deepseek-model') || '').trim())
|
|
3087
|
+
.filter(Boolean);
|
|
3088
|
+
const claudeApiCheckboxes = Array.from(body.querySelectorAll('.model-variant-checkbox[data-claude-api-model]'));
|
|
3089
|
+
const selectedClaudeApiModels = claudeApiCheckboxes
|
|
3090
|
+
.filter((input) => input.checked)
|
|
3091
|
+
.map((input) => String(input.getAttribute('data-claude-api-model') || '').trim())
|
|
3092
|
+
.filter(Boolean);
|
|
2926
3093
|
// Only the active provider tab's rows are in the DOM, but the PATCH
|
|
2927
3094
|
// replaces the WHOLE enabled set — so the save must only change rows the
|
|
2928
3095
|
// user could actually see. Off-screen variants keep their stored state,
|
|
2929
3096
|
// otherwise saving from e.g. the OpenAI tab silently disables every
|
|
2930
3097
|
// Copilot-only vendor group (Anthropic, Google, …).
|
|
2931
|
-
const renderedVariantCheckboxes = Array.from(body.querySelectorAll('.model-variant-checkbox[data-selectable="1"]:not([data-claude-model]):not([data-cursor-model]):not([data-grok-model])'));
|
|
3098
|
+
const renderedVariantCheckboxes = Array.from(body.querySelectorAll('.model-variant-checkbox[data-selectable="1"]:not([data-claude-model]):not([data-cursor-model]):not([data-grok-model]):not([data-deepseek-model]):not([data-claude-api-model])'));
|
|
2932
3099
|
const renderedVariantIds = new Set(renderedVariantCheckboxes
|
|
2933
3100
|
.map((input) => String(input.getAttribute('data-variant-id') || '').trim())
|
|
2934
3101
|
.filter(Boolean));
|
|
@@ -2969,7 +3136,18 @@ async function saveSelectedModelsFromModal() {
|
|
|
2969
3136
|
if (!savedGrok) throw new Error('Failed to save Grok model selection');
|
|
2970
3137
|
applyGrokSettingsState(savedGrok);
|
|
2971
3138
|
}
|
|
2972
|
-
if (
|
|
3139
|
+
if (deepseekCheckboxes.length) {
|
|
3140
|
+
const savedDeepSeek = await updateDeepSeekSettings({ enabledModels: selectedDeepSeekModels });
|
|
3141
|
+
if (!savedDeepSeek) throw new Error('Failed to save DeepSeek model selection');
|
|
3142
|
+
applyDeepSeekSettingsState(savedDeepSeek);
|
|
3143
|
+
}
|
|
3144
|
+
if (claudeApiCheckboxes.length) {
|
|
3145
|
+
const savedClaudeApi = await updateClaudeApiSettings({ enabledModels: selectedClaudeApiModels });
|
|
3146
|
+
if (!savedClaudeApi) throw new Error('Failed to save Claude API model selection');
|
|
3147
|
+
applyClaudeApiSettingsState(savedClaudeApi);
|
|
3148
|
+
}
|
|
3149
|
+
if (claudeCheckboxes.length || cursorCheckboxes.length || grokCheckboxes.length || deepseekCheckboxes.length
|
|
3150
|
+
|| claudeApiCheckboxes.length) {
|
|
2973
3151
|
const refreshedCatalog = await loadModelVariantCatalog();
|
|
2974
3152
|
if (refreshedCatalog) applyModelVariantCatalogState(refreshedCatalog);
|
|
2975
3153
|
}
|
|
@@ -3476,11 +3654,15 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3476
3654
|
const refreshLookupId = sessionId || trimmedConvId || null;
|
|
3477
3655
|
const providerLabel = payload.providerType === 'claude'
|
|
3478
3656
|
? 'Claude'
|
|
3479
|
-
: (payload.providerType ===
|
|
3480
|
-
? 'Claude
|
|
3481
|
-
: (payload.providerType ===
|
|
3482
|
-
? '
|
|
3483
|
-
: (payload.providerType === '
|
|
3657
|
+
: (payload.providerType === 'claude-api'
|
|
3658
|
+
? 'Claude API'
|
|
3659
|
+
: (payload.providerType === CLAUDE_CLOUD_PROVIDER
|
|
3660
|
+
? 'Claude Cloud'
|
|
3661
|
+
: (payload.providerType === 'cursor'
|
|
3662
|
+
? 'Cursor'
|
|
3663
|
+
: (payload.providerType === 'grok'
|
|
3664
|
+
? 'Grok'
|
|
3665
|
+
: (payload.providerType === 'deepseek' ? 'DeepSeek' : 'Copilot')))));
|
|
3484
3666
|
const subtitle = sessionId
|
|
3485
3667
|
? `${providerLabel} session ${sessionId.slice(0, 8)}`
|
|
3486
3668
|
: (trimmedConvId ? `Conversation ${trimmedConvId.slice(0, 8)}` : 'No conversation selected');
|
|
@@ -3489,14 +3671,18 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3489
3671
|
// (older relays send neither, and the notes fall back to draft vs stored).
|
|
3490
3672
|
const sessionField = (key) => payload[key] ?? payload.contextUsage?.[key] ?? null;
|
|
3491
3673
|
const modelContextLimit = sessionField('modelContextLimit');
|
|
3674
|
+
// A DeepSeek or Claude API chat runs the same Claude Code runner, so it has
|
|
3675
|
+
// the same compaction window to show and to steer.
|
|
3676
|
+
const runsClaudeCode = payload.providerType === 'claude' || payload.providerType === 'deepseek'
|
|
3677
|
+
|| payload.providerType === 'claude-api';
|
|
3492
3678
|
const usageHtml = renderContextUsageHtml(payload.contextUsage, {
|
|
3493
|
-
compactionWindow:
|
|
3679
|
+
compactionWindow: runsClaudeCode,
|
|
3494
3680
|
modelContextLimit,
|
|
3495
3681
|
});
|
|
3496
3682
|
// The slider's writes need the conversation id even when the modal was
|
|
3497
3683
|
// opened through an sdk_session_id.
|
|
3498
3684
|
const boundConversationId = String(
|
|
3499
|
-
payload.resolvedConversationId || (
|
|
3685
|
+
payload.resolvedConversationId || (runsClaudeCode ? trimmedConvId : '') || '',
|
|
3500
3686
|
).trim();
|
|
3501
3687
|
// A draft for another conversation is saved before this one takes over.
|
|
3502
3688
|
if (autoCompactDraft.conversationId && autoCompactDraft.conversationId !== boundConversationId) {
|
|
@@ -3512,9 +3698,9 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3512
3698
|
usedTokens: payload.contextUsage?.totalTokens ?? null,
|
|
3513
3699
|
};
|
|
3514
3700
|
const draftWindow = autoCompactDraft.conversationId === boundConversationId ? autoCompactDraft.window : undefined;
|
|
3515
|
-
// Claude
|
|
3516
|
-
// session-settings surface) to steer.
|
|
3517
|
-
const autoCompactHtml =
|
|
3701
|
+
// Claude Code only (Claude and DeepSeek chats): no other provider exposes a
|
|
3702
|
+
// compaction window (or any session-settings surface) to steer.
|
|
3703
|
+
const autoCompactHtml = runsClaudeCode
|
|
3518
3704
|
? renderAutoCompactControlHtml({
|
|
3519
3705
|
autoCompactWindow: payload.autoCompactWindow ?? null,
|
|
3520
3706
|
autoCompactThreshold: payload.contextUsage?.autoCompactThreshold ?? null,
|
|
@@ -3534,15 +3720,17 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3534
3720
|
usedTokens: autoCompactControlSession.usedTokens,
|
|
3535
3721
|
})
|
|
3536
3722
|
: '';
|
|
3537
|
-
const thinkingHtml = payload.providerType === 'claude'
|
|
3723
|
+
const thinkingHtml = payload.providerType === 'claude' || payload.providerType === 'claude-api'
|
|
3538
3724
|
? renderThinkingControlHtml({
|
|
3539
3725
|
thinkingEnabled: payload.thinkingEnabled !== false,
|
|
3540
3726
|
thinkingDisplay: payload.thinkingDisplay ?? null,
|
|
3541
3727
|
})
|
|
3542
3728
|
: '';
|
|
3543
|
-
// What this folder's commits say, with the folder override: Claude
|
|
3544
|
-
// Claude Cloud chat that was started from a folder
|
|
3545
|
-
|
|
3729
|
+
// What this folder's commits say, with the folder override: Claude (on
|
|
3730
|
+
// either billing), and a Claude Cloud chat that was started from a folder
|
|
3731
|
+
// (no path, no row).
|
|
3732
|
+
const attributionHtml = payload.providerType === 'claude' || payload.providerType === 'claude-api'
|
|
3733
|
+
|| payload.providerType === 'claude-cloud'
|
|
3546
3734
|
? renderAttributionControlHtml(payload.attribution || null)
|
|
3547
3735
|
: '';
|
|
3548
3736
|
thinkingControlStored = {
|
|
@@ -4571,6 +4759,8 @@ initSocketHandlers({
|
|
|
4571
4759
|
applyCopilotSettingsState,
|
|
4572
4760
|
applyGrokSettingsState,
|
|
4573
4761
|
applyCursorSettingsState,
|
|
4762
|
+
applyDeepSeekSettingsState,
|
|
4763
|
+
applyClaudeApiSettingsState,
|
|
4574
4764
|
});
|
|
4575
4765
|
|
|
4576
4766
|
initCwdPicker({
|
|
@@ -4655,6 +4845,15 @@ async function initApp() {
|
|
|
4655
4845
|
syncWindowRetentionSlider();
|
|
4656
4846
|
initHostSuspendUi();
|
|
4657
4847
|
initUsageLimitUi();
|
|
4848
|
+
// After a switch the chat's provider changed: its model list, lock note
|
|
4849
|
+
// and list pill follow.
|
|
4850
|
+
initClaudeBillingToggle({
|
|
4851
|
+
isSharedReaderMode,
|
|
4852
|
+
onConversationProviderChanged: (conversationId) => {
|
|
4853
|
+
if (conversationId === currentConvId) syncAutoModelAvailability();
|
|
4854
|
+
renderConvList();
|
|
4855
|
+
},
|
|
4856
|
+
});
|
|
4658
4857
|
}
|
|
4659
4858
|
setupViewportTracking();
|
|
4660
4859
|
bindChatSelectionGuard();
|
|
@@ -4953,11 +5152,17 @@ async function initApp() {
|
|
|
4953
5152
|
// Loaded here for provider readiness: whether any provider can run a chat
|
|
4954
5153
|
// decides what the empty screen, New Chat and the composer offer.
|
|
4955
5154
|
const claudeSettings = await refreshClaudeSettingsState();
|
|
5155
|
+
// null on a relay without DeepSeek: not offered, as for Claude Cloud.
|
|
5156
|
+
const deepseekSettings = await refreshDeepSeekSettingsState();
|
|
5157
|
+
// The same for Claude API; its state also decides the composer's billing toggle.
|
|
5158
|
+
const claudeApiSettings = await refreshClaudeApiSettingsState();
|
|
4956
5159
|
updateProviderReadinessInputs({
|
|
4957
5160
|
openai: openAISettings || null,
|
|
4958
5161
|
cursor: cursorSettings || null,
|
|
4959
5162
|
grok: grokSettings || null,
|
|
4960
5163
|
claude: claudeSettings || null,
|
|
5164
|
+
deepseek: deepseekSettings || null,
|
|
5165
|
+
claudeApi: claudeApiSettings || null,
|
|
4961
5166
|
});
|
|
4962
5167
|
// Not awaited: only a Claude Cloud conversation needs it (its model list),
|
|
4963
5168
|
// and the subscription below rebuilds that list when the answer arrives. A
|
|
@@ -5133,6 +5338,15 @@ window.cancelRunningUpdate = cancelRunningUpdate;
|
|
|
5133
5338
|
window.dismissAvailableUpdate = dismissAvailableUpdate;
|
|
5134
5339
|
window.dismissUpdateOutcome = dismissUpdateOutcome;
|
|
5135
5340
|
window.saveGrokSettings = saveGrokSettings;
|
|
5341
|
+
window.saveDeepSeekSettings = saveDeepSeekSettings;
|
|
5342
|
+
window.removeDeepSeekSettings = removeDeepSeekSettings;
|
|
5343
|
+
window.toggleDeepSeekProvider = toggleDeepSeekProvider;
|
|
5344
|
+
window.importDeepSeekUsageExport = importDeepSeekUsageExport;
|
|
5345
|
+
window.saveClaudeApiSettings = saveClaudeApiSettings;
|
|
5346
|
+
window.removeClaudeApiSettings = removeClaudeApiSettings;
|
|
5347
|
+
window.toggleClaudeApiProvider = toggleClaudeApiProvider;
|
|
5348
|
+
window.refreshClaudeApiModels = refreshClaudeApiModels;
|
|
5349
|
+
window.openClaudeTabFromClaudeApi = openClaudeTabFromClaudeApi;
|
|
5136
5350
|
window.toggleGrokProvider = toggleGrokProvider;
|
|
5137
5351
|
window.saveClaudeCloudSettings = saveClaudeCloudSettings;
|
|
5138
5352
|
window.toggleClaudeCloudProvider = toggleClaudeCloudProvider;
|