@oar-sh/oar 0.9.16 → 0.9.17
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/CHANGELOG.md +220 -0
- package/README.md +12 -11
- package/package.json +2 -1
- package/server/claude-worker/claude-attachments.mjs +7 -4
- package/server/claude-worker/claude-sdk-adapter.mjs +167 -9
- package/server/claude-worker/claude-session-process.mjs +127 -29
- package/server/claude-worker/claude-session-worker-main.mjs +266 -0
- package/server/claude-worker/claude-session-worker.mjs +2 -253
- package/server/claude-worker/claude-turn-failure.mjs +8 -0
- package/server/claude-worker/claude-turn-publisher.mjs +282 -13
- package/server/claude-worker/claude-usage-totals.mjs +108 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +55 -0
- package/server/copilot-worker/copilot-sdk-adapter.mjs +18 -4
- package/server/copilot-worker/copilot-sdk-session-process.mjs +71 -5
- package/server/db-schema.mjs +63 -0
- package/server/deepseek-worker/deepseek-claude-backend.mjs +433 -0
- package/server/deepseek-worker/deepseek-session-worker.mjs +37 -0
- 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/public/app/api-client.js +53 -0
- package/server/public/app/bootstrap.js +101 -13
- package/server/public/app/context-tier-options.mjs +4 -0
- package/server/public/app/conversation-provider-indicator.mjs +7 -0
- package/server/public/app/conversation-view.js +2 -0
- package/server/public/app/deepseek-settings-ui.js +342 -0
- package/server/public/app/inbox-ui.js +14 -3
- package/server/public/app/journal-view.js +31 -3
- package/server/public/app/model-selector-options.mjs +11 -0
- package/server/public/app/model-vision-support.mjs +2 -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 +4 -1
- package/server/public/app/relay-error-ctas.mjs +8 -0
- package/server/public/app/settings-modal.js +11 -0
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +10 -0
- package/server/public/index.html +408 -33
- package/server/repositories/conversation-group-repository.mjs +13 -6
- package/server/routes/conversation-group-routes.mjs +2 -1
- package/server/routes/deepseek-usage-import-routes.mjs +72 -0
- package/server/routes/messages-routes.mjs +192 -15
- package/server/routes/sessions-routes.mjs +364 -55
- package/server/server-runtime.mjs +123 -10
- package/server/services/about-service.mjs +1 -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-contract.mjs +100 -5
- package/server/services/plan-usage-deepseek.mjs +931 -0
- package/server/services/plan-usage-service.mjs +431 -0
- package/server/services/provider-model-selection.mjs +1 -0
- package/server/services/provider-reasoning-effort.mjs +15 -2
- package/server/services/remote-relay-dispatcher.mjs +2 -1
- package/server/services/session-worker-launch-service.mjs +233 -82
- package/server/services/worker-secret-redaction.mjs +318 -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 +2 -2
- package/shared/remote-relay-contract.mjs +1 -1
- package/shared/steer-settle-failure.mjs +1 -0
- package/shared/worker-runtime/direct-openai-image.mjs +128 -0
- package/shared/worker-runtime/worker-log-file.mjs +60 -0
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
updateClaudeSettings,
|
|
80
80
|
updateCursorSettings,
|
|
81
81
|
updateGrokSettings,
|
|
82
|
+
updateDeepSeekSettings,
|
|
82
83
|
loadConversation,
|
|
83
84
|
refreshConversationHistory,
|
|
84
85
|
updateConversationTitle,
|
|
@@ -323,6 +324,12 @@ import {
|
|
|
323
324
|
toggleGrokProvider,
|
|
324
325
|
applyGrokSettingsState,
|
|
325
326
|
refreshGrokSettingsState,
|
|
327
|
+
saveDeepSeekSettings,
|
|
328
|
+
removeDeepSeekSettings,
|
|
329
|
+
toggleDeepSeekProvider,
|
|
330
|
+
importDeepSeekUsageExport,
|
|
331
|
+
applyDeepSeekSettingsState,
|
|
332
|
+
refreshDeepSeekSettingsState,
|
|
326
333
|
saveCursorSettings,
|
|
327
334
|
removeCursorSettings,
|
|
328
335
|
saveCursorAllowanceSettings,
|
|
@@ -445,6 +452,7 @@ const PROVIDER_LABELS = {
|
|
|
445
452
|
'claude-cloud': 'Claude Cloud',
|
|
446
453
|
cursor: 'Cursor SDK',
|
|
447
454
|
grok: 'Grok',
|
|
455
|
+
deepseek: 'DeepSeek',
|
|
448
456
|
'github-copilot': 'GitHub Copilot',
|
|
449
457
|
// Vendor grouping for Copilot-served rows, distinct from the Claude SDK runtime.
|
|
450
458
|
anthropic: 'Anthropic',
|
|
@@ -1505,6 +1513,7 @@ function normalizeModelSelectorProviderType(providerType = '') {
|
|
|
1505
1513
|
if (normalized === CLAUDE_CLOUD_PROVIDER) return CLAUDE_CLOUD_PROVIDER;
|
|
1506
1514
|
if (normalized === 'cursor') return 'cursor';
|
|
1507
1515
|
if (normalized === 'grok') return 'grok';
|
|
1516
|
+
if (normalized === 'deepseek') return 'deepseek';
|
|
1508
1517
|
return 'github';
|
|
1509
1518
|
}
|
|
1510
1519
|
|
|
@@ -1532,6 +1541,7 @@ function modelVisibleForActiveProvider(modelId, activeProviderType, providersByM
|
|
|
1532
1541
|
const hasClaude = providers.includes('claude');
|
|
1533
1542
|
const hasCursor = providers.includes('cursor');
|
|
1534
1543
|
const hasGrok = providers.includes('grok');
|
|
1544
|
+
const hasDeepSeek = providers.includes('deepseek');
|
|
1535
1545
|
// Only present if a relay tags catalog rows for Claude Cloud; such a row is
|
|
1536
1546
|
// then as exclusive as the other runtimes' rows.
|
|
1537
1547
|
const hasClaudeCloud = providers.includes(CLAUDE_CLOUD_PROVIDER);
|
|
@@ -1540,6 +1550,7 @@ function modelVisibleForActiveProvider(modelId, activeProviderType, providersByM
|
|
|
1540
1550
|
&& provider !== 'claude'
|
|
1541
1551
|
&& provider !== 'cursor'
|
|
1542
1552
|
&& provider !== 'grok'
|
|
1553
|
+
&& provider !== 'deepseek'
|
|
1543
1554
|
&& provider !== CLAUDE_CLOUD_PROVIDER
|
|
1544
1555
|
));
|
|
1545
1556
|
const activeProvider = normalizeModelSelectorProviderType(activeProviderType);
|
|
@@ -1547,7 +1558,8 @@ function modelVisibleForActiveProvider(modelId, activeProviderType, providersByM
|
|
|
1547
1558
|
if (activeProvider === 'claude') return hasClaude;
|
|
1548
1559
|
if (activeProvider === 'cursor') return hasCursor;
|
|
1549
1560
|
if (activeProvider === 'grok') return hasGrok;
|
|
1550
|
-
|
|
1561
|
+
if (activeProvider === 'deepseek') return hasDeepSeek;
|
|
1562
|
+
return !((hasOpenAIByok || hasClaude || hasCursor || hasGrok || hasDeepSeek || hasClaudeCloud) && !hasNonExclusiveProvider);
|
|
1551
1563
|
}
|
|
1552
1564
|
|
|
1553
1565
|
// A Claude Cloud conversation does not pick from the shared catalog: its
|
|
@@ -1990,6 +2002,7 @@ const RELAY_MODES_BY_PROVIDER = {
|
|
|
1990
2002
|
claude: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1991
2003
|
cursor: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1992
2004
|
grok: ['agent', 'ask', 'plan', 'autopilot'],
|
|
2005
|
+
deepseek: ['agent', 'ask', 'plan', 'autopilot'],
|
|
1993
2006
|
'claude-cloud': ['agent'],
|
|
1994
2007
|
};
|
|
1995
2008
|
|
|
@@ -2336,6 +2349,13 @@ function reportOpenAIModelDiscoveryFailure(payload) {
|
|
|
2336
2349
|
8000,
|
|
2337
2350
|
);
|
|
2338
2351
|
}
|
|
2352
|
+
const deepseekDiscovery = payload?.deepseekModelDiscovery;
|
|
2353
|
+
if (deepseekDiscovery && deepseekDiscovery.ok === false && !deepseekDiscovery.skipped) {
|
|
2354
|
+
showTransientRelayNotice(
|
|
2355
|
+
`DeepSeek model discovery failed. Cached DeepSeek models were kept: ${deepseekDiscovery.error || 'unknown error'}`,
|
|
2356
|
+
8000,
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2339
2359
|
}
|
|
2340
2360
|
|
|
2341
2361
|
async function retryModelMetadataRefresh() {
|
|
@@ -2473,6 +2493,17 @@ function applyModelVariantCatalogState(payload) {
|
|
|
2473
2493
|
: [],
|
|
2474
2494
|
}
|
|
2475
2495
|
: null,
|
|
2496
|
+
deepseekModels: payload?.deepseekModels && typeof payload.deepseekModels === 'object'
|
|
2497
|
+
? {
|
|
2498
|
+
defaultModel: String(payload.deepseekModels.defaultModel || '').trim(),
|
|
2499
|
+
availableModels: Array.isArray(payload.deepseekModels.availableModels)
|
|
2500
|
+
? payload.deepseekModels.availableModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2501
|
+
: [],
|
|
2502
|
+
enabledModels: Array.isArray(payload.deepseekModels.enabledModels)
|
|
2503
|
+
? payload.deepseekModels.enabledModels.map((value) => String(value || '').trim()).filter(Boolean)
|
|
2504
|
+
: [],
|
|
2505
|
+
}
|
|
2506
|
+
: null,
|
|
2476
2507
|
};
|
|
2477
2508
|
}
|
|
2478
2509
|
|
|
@@ -2549,6 +2580,10 @@ function renderModelVariantCatalogBody() {
|
|
|
2549
2580
|
return entryProvider === 'grok';
|
|
2550
2581
|
}
|
|
2551
2582
|
if (entryProvider === 'grok') return false;
|
|
2583
|
+
if (activeTab === 'deepseek') {
|
|
2584
|
+
return entryProvider === 'deepseek';
|
|
2585
|
+
}
|
|
2586
|
+
if (entryProvider === 'deepseek') return false;
|
|
2552
2587
|
if (activeTab === 'openai') {
|
|
2553
2588
|
return hasOpenAIByok || entryProvider === 'openai-byok';
|
|
2554
2589
|
}
|
|
@@ -2560,6 +2595,7 @@ function renderModelVariantCatalogBody() {
|
|
|
2560
2595
|
&& provider !== 'claude'
|
|
2561
2596
|
&& provider !== 'cursor'
|
|
2562
2597
|
&& provider !== 'grok'
|
|
2598
|
+
&& provider !== 'deepseek'
|
|
2563
2599
|
&& provider !== CLAUDE_CLOUD_PROVIDER
|
|
2564
2600
|
));
|
|
2565
2601
|
}
|
|
@@ -2670,6 +2706,29 @@ function renderModelVariantCatalogBody() {
|
|
|
2670
2706
|
}
|
|
2671
2707
|
grouped.set('grok', providerRows);
|
|
2672
2708
|
}
|
|
2709
|
+
const deepseekCatalog = modelVariantCatalogState.deepseekModels;
|
|
2710
|
+
if (deepseekCatalog?.availableModels?.length) {
|
|
2711
|
+
const deepseekEnabledSet = new Set(deepseekCatalog.enabledModels || []);
|
|
2712
|
+
const deepseekDefaultModel = String(deepseekCatalog.defaultModel || '').trim();
|
|
2713
|
+
const providerRows = [];
|
|
2714
|
+
for (const [index, deepseekModelId] of deepseekCatalog.availableModels.entries()) {
|
|
2715
|
+
const isDefaultModel = deepseekModelId === deepseekDefaultModel;
|
|
2716
|
+
providerRows.push({
|
|
2717
|
+
variantId: `${deepseekModelId}--provider-deepseek`,
|
|
2718
|
+
baseModelId: deepseekModelId,
|
|
2719
|
+
provider: 'deepseek',
|
|
2720
|
+
label: humanizeModelLabel(deepseekModelId),
|
|
2721
|
+
releaseStatus: null,
|
|
2722
|
+
reasoningEffort: null,
|
|
2723
|
+
// The default model always stays enabled, as for the other providers.
|
|
2724
|
+
selectable: !isDefaultModel,
|
|
2725
|
+
enabled: deepseekEnabledSet.has(deepseekModelId) || isDefaultModel,
|
|
2726
|
+
deepseekModelId,
|
|
2727
|
+
sortOrder: index,
|
|
2728
|
+
});
|
|
2729
|
+
}
|
|
2730
|
+
grouped.set('deepseek', providerRows);
|
|
2731
|
+
}
|
|
2673
2732
|
const selected = new Set(modelVariantCatalogState.enabledVariantIds);
|
|
2674
2733
|
const selectedOrder = new Map(
|
|
2675
2734
|
modelVariantCatalogState.enabledVariantIds.map((variantId, index) => [variantId, index]),
|
|
@@ -2705,15 +2764,18 @@ function renderModelVariantCatalogBody() {
|
|
|
2705
2764
|
const hasAnthropicTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'anthropic'));
|
|
2706
2765
|
const hasCursorTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'cursor'));
|
|
2707
2766
|
const hasGrokTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'grok'));
|
|
2767
|
+
const hasDeepSeekTab = providerOrder.some((providerKey) => providerBelongsToTab(providerKey, 'deepseek'));
|
|
2708
2768
|
if (modelVariantCatalogProviderTab === 'openai' && !hasOpenAITab) modelVariantCatalogProviderTab = 'copilot';
|
|
2709
2769
|
if (modelVariantCatalogProviderTab === 'anthropic' && !hasAnthropicTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2710
2770
|
if (modelVariantCatalogProviderTab === 'cursor' && !hasCursorTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2711
2771
|
if (modelVariantCatalogProviderTab === 'grok' && !hasGrokTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2772
|
+
if (modelVariantCatalogProviderTab === 'deepseek' && !hasDeepSeekTab) modelVariantCatalogProviderTab = 'copilot';
|
|
2712
2773
|
if (modelVariantCatalogProviderTab === 'copilot' && !hasCopilotTab) {
|
|
2713
2774
|
if (hasOpenAITab) modelVariantCatalogProviderTab = 'openai';
|
|
2714
2775
|
else if (hasAnthropicTab) modelVariantCatalogProviderTab = 'anthropic';
|
|
2715
2776
|
else if (hasCursorTab) modelVariantCatalogProviderTab = 'cursor';
|
|
2716
2777
|
else if (hasGrokTab) modelVariantCatalogProviderTab = 'grok';
|
|
2778
|
+
else if (hasDeepSeekTab) modelVariantCatalogProviderTab = 'deepseek';
|
|
2717
2779
|
}
|
|
2718
2780
|
const visibleProviderOrder = providerOrder.filter((providerKey) => providerBelongsToTab(providerKey, modelVariantCatalogProviderTab));
|
|
2719
2781
|
const providerTabButtons = `
|
|
@@ -2723,6 +2785,7 @@ function renderModelVariantCatalogBody() {
|
|
|
2723
2785
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'anthropic' ? ' active' : ''}" data-model-provider-tab="anthropic" ${hasAnthropicTab ? '' : 'disabled'}>Claude SDK</button>
|
|
2724
2786
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'cursor' ? ' active' : ''}" data-model-provider-tab="cursor" ${hasCursorTab ? '' : 'disabled'}>Cursor SDK</button>
|
|
2725
2787
|
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'grok' ? ' active' : ''}" data-model-provider-tab="grok" ${hasGrokTab ? '' : 'disabled'}>Grok</button>
|
|
2788
|
+
<button type="button" class="summary-btn model-provider-tab${modelVariantCatalogProviderTab === 'deepseek' ? ' active' : ''}" data-model-provider-tab="deepseek" ${hasDeepSeekTab ? '' : 'disabled'}>DeepSeek</button>
|
|
2726
2789
|
</div>
|
|
2727
2790
|
`;
|
|
2728
2791
|
const warnings = [
|
|
@@ -2775,9 +2838,9 @@ function renderModelVariantCatalogBody() {
|
|
|
2775
2838
|
: '';
|
|
2776
2839
|
const variantRowsHtml = variantRows.map((row) => {
|
|
2777
2840
|
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 || '';
|
|
2841
|
+
// SDK-provider rows (Claude/Cursor/Grok/DeepSeek) carry their own enabled
|
|
2842
|
+
// flag; only Copilot variant rows read the enabledVariantIds selection.
|
|
2843
|
+
const sdkProviderModelId = row.claudeModelId || row.cursorModelId || row.grokModelId || row.deepseekModelId || '';
|
|
2781
2844
|
const checked = sdkProviderModelId
|
|
2782
2845
|
? row.enabled === true
|
|
2783
2846
|
: (selectable && selected.has(row.variantId));
|
|
@@ -2797,7 +2860,9 @@ function renderModelVariantCatalogBody() {
|
|
|
2797
2860
|
? ` data-claude-model="${escHtml(row.claudeModelId)}"`
|
|
2798
2861
|
: (row.cursorModelId
|
|
2799
2862
|
? ` data-cursor-model="${escHtml(row.cursorModelId)}"`
|
|
2800
|
-
: (row.grokModelId
|
|
2863
|
+
: (row.grokModelId
|
|
2864
|
+
? ` data-grok-model="${escHtml(row.grokModelId)}"`
|
|
2865
|
+
: (row.deepseekModelId ? ` data-deepseek-model="${escHtml(row.deepseekModelId)}"` : '')));
|
|
2801
2866
|
return `
|
|
2802
2867
|
<label class="model-variant-row">
|
|
2803
2868
|
<input class="model-variant-checkbox" type="checkbox" data-selectable="${selectable ? '1' : '0'}" data-variant-id="${escHtml(row.variantId)}"${sdkModelAttr} ${checked ? 'checked' : ''} ${selectable ? '' : 'disabled'}>
|
|
@@ -2923,12 +2988,17 @@ async function saveSelectedModelsFromModal() {
|
|
|
2923
2988
|
.filter((input) => input.checked)
|
|
2924
2989
|
.map((input) => String(input.getAttribute('data-grok-model') || '').trim())
|
|
2925
2990
|
.filter(Boolean);
|
|
2991
|
+
const deepseekCheckboxes = Array.from(body.querySelectorAll('.model-variant-checkbox[data-deepseek-model]'));
|
|
2992
|
+
const selectedDeepSeekModels = deepseekCheckboxes
|
|
2993
|
+
.filter((input) => input.checked)
|
|
2994
|
+
.map((input) => String(input.getAttribute('data-deepseek-model') || '').trim())
|
|
2995
|
+
.filter(Boolean);
|
|
2926
2996
|
// Only the active provider tab's rows are in the DOM, but the PATCH
|
|
2927
2997
|
// replaces the WHOLE enabled set — so the save must only change rows the
|
|
2928
2998
|
// user could actually see. Off-screen variants keep their stored state,
|
|
2929
2999
|
// otherwise saving from e.g. the OpenAI tab silently disables every
|
|
2930
3000
|
// 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])'));
|
|
3001
|
+
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])'));
|
|
2932
3002
|
const renderedVariantIds = new Set(renderedVariantCheckboxes
|
|
2933
3003
|
.map((input) => String(input.getAttribute('data-variant-id') || '').trim())
|
|
2934
3004
|
.filter(Boolean));
|
|
@@ -2969,7 +3039,12 @@ async function saveSelectedModelsFromModal() {
|
|
|
2969
3039
|
if (!savedGrok) throw new Error('Failed to save Grok model selection');
|
|
2970
3040
|
applyGrokSettingsState(savedGrok);
|
|
2971
3041
|
}
|
|
2972
|
-
if (
|
|
3042
|
+
if (deepseekCheckboxes.length) {
|
|
3043
|
+
const savedDeepSeek = await updateDeepSeekSettings({ enabledModels: selectedDeepSeekModels });
|
|
3044
|
+
if (!savedDeepSeek) throw new Error('Failed to save DeepSeek model selection');
|
|
3045
|
+
applyDeepSeekSettingsState(savedDeepSeek);
|
|
3046
|
+
}
|
|
3047
|
+
if (claudeCheckboxes.length || cursorCheckboxes.length || grokCheckboxes.length || deepseekCheckboxes.length) {
|
|
2973
3048
|
const refreshedCatalog = await loadModelVariantCatalog();
|
|
2974
3049
|
if (refreshedCatalog) applyModelVariantCatalogState(refreshedCatalog);
|
|
2975
3050
|
}
|
|
@@ -3480,7 +3555,9 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3480
3555
|
? 'Claude Cloud'
|
|
3481
3556
|
: (payload.providerType === 'cursor'
|
|
3482
3557
|
? 'Cursor'
|
|
3483
|
-
: (payload.providerType === 'grok'
|
|
3558
|
+
: (payload.providerType === 'grok'
|
|
3559
|
+
? 'Grok'
|
|
3560
|
+
: (payload.providerType === 'deepseek' ? 'DeepSeek' : 'Copilot'))));
|
|
3484
3561
|
const subtitle = sessionId
|
|
3485
3562
|
? `${providerLabel} session ${sessionId.slice(0, 8)}`
|
|
3486
3563
|
: (trimmedConvId ? `Conversation ${trimmedConvId.slice(0, 8)}` : 'No conversation selected');
|
|
@@ -3489,14 +3566,17 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3489
3566
|
// (older relays send neither, and the notes fall back to draft vs stored).
|
|
3490
3567
|
const sessionField = (key) => payload[key] ?? payload.contextUsage?.[key] ?? null;
|
|
3491
3568
|
const modelContextLimit = sessionField('modelContextLimit');
|
|
3569
|
+
// A DeepSeek chat runs the same Claude Code runner, so it has the same
|
|
3570
|
+
// compaction window to show and to steer.
|
|
3571
|
+
const runsClaudeCode = payload.providerType === 'claude' || payload.providerType === 'deepseek';
|
|
3492
3572
|
const usageHtml = renderContextUsageHtml(payload.contextUsage, {
|
|
3493
|
-
compactionWindow:
|
|
3573
|
+
compactionWindow: runsClaudeCode,
|
|
3494
3574
|
modelContextLimit,
|
|
3495
3575
|
});
|
|
3496
3576
|
// The slider's writes need the conversation id even when the modal was
|
|
3497
3577
|
// opened through an sdk_session_id.
|
|
3498
3578
|
const boundConversationId = String(
|
|
3499
|
-
payload.resolvedConversationId || (
|
|
3579
|
+
payload.resolvedConversationId || (runsClaudeCode ? trimmedConvId : '') || '',
|
|
3500
3580
|
).trim();
|
|
3501
3581
|
// A draft for another conversation is saved before this one takes over.
|
|
3502
3582
|
if (autoCompactDraft.conversationId && autoCompactDraft.conversationId !== boundConversationId) {
|
|
@@ -3512,9 +3592,9 @@ async function loadContextSummaryAndRender(convId) {
|
|
|
3512
3592
|
usedTokens: payload.contextUsage?.totalTokens ?? null,
|
|
3513
3593
|
};
|
|
3514
3594
|
const draftWindow = autoCompactDraft.conversationId === boundConversationId ? autoCompactDraft.window : undefined;
|
|
3515
|
-
// Claude
|
|
3516
|
-
// session-settings surface) to steer.
|
|
3517
|
-
const autoCompactHtml =
|
|
3595
|
+
// Claude Code only (Claude and DeepSeek chats): no other provider exposes a
|
|
3596
|
+
// compaction window (or any session-settings surface) to steer.
|
|
3597
|
+
const autoCompactHtml = runsClaudeCode
|
|
3518
3598
|
? renderAutoCompactControlHtml({
|
|
3519
3599
|
autoCompactWindow: payload.autoCompactWindow ?? null,
|
|
3520
3600
|
autoCompactThreshold: payload.contextUsage?.autoCompactThreshold ?? null,
|
|
@@ -4571,6 +4651,7 @@ initSocketHandlers({
|
|
|
4571
4651
|
applyCopilotSettingsState,
|
|
4572
4652
|
applyGrokSettingsState,
|
|
4573
4653
|
applyCursorSettingsState,
|
|
4654
|
+
applyDeepSeekSettingsState,
|
|
4574
4655
|
});
|
|
4575
4656
|
|
|
4576
4657
|
initCwdPicker({
|
|
@@ -4953,11 +5034,14 @@ async function initApp() {
|
|
|
4953
5034
|
// Loaded here for provider readiness: whether any provider can run a chat
|
|
4954
5035
|
// decides what the empty screen, New Chat and the composer offer.
|
|
4955
5036
|
const claudeSettings = await refreshClaudeSettingsState();
|
|
5037
|
+
// null on a relay without DeepSeek: not offered, as for Claude Cloud.
|
|
5038
|
+
const deepseekSettings = await refreshDeepSeekSettingsState();
|
|
4956
5039
|
updateProviderReadinessInputs({
|
|
4957
5040
|
openai: openAISettings || null,
|
|
4958
5041
|
cursor: cursorSettings || null,
|
|
4959
5042
|
grok: grokSettings || null,
|
|
4960
5043
|
claude: claudeSettings || null,
|
|
5044
|
+
deepseek: deepseekSettings || null,
|
|
4961
5045
|
});
|
|
4962
5046
|
// Not awaited: only a Claude Cloud conversation needs it (its model list),
|
|
4963
5047
|
// and the subscription below rebuilds that list when the answer arrives. A
|
|
@@ -5133,6 +5217,10 @@ window.cancelRunningUpdate = cancelRunningUpdate;
|
|
|
5133
5217
|
window.dismissAvailableUpdate = dismissAvailableUpdate;
|
|
5134
5218
|
window.dismissUpdateOutcome = dismissUpdateOutcome;
|
|
5135
5219
|
window.saveGrokSettings = saveGrokSettings;
|
|
5220
|
+
window.saveDeepSeekSettings = saveDeepSeekSettings;
|
|
5221
|
+
window.removeDeepSeekSettings = removeDeepSeekSettings;
|
|
5222
|
+
window.toggleDeepSeekProvider = toggleDeepSeekProvider;
|
|
5223
|
+
window.importDeepSeekUsageExport = importDeepSeekUsageExport;
|
|
5136
5224
|
window.toggleGrokProvider = toggleGrokProvider;
|
|
5137
5225
|
window.saveClaudeCloudSettings = saveClaudeCloudSettings;
|
|
5138
5226
|
window.toggleClaudeCloudProvider = toggleClaudeCloudProvider;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Mirrors shared/model-id.mjs, which the browser cannot import (only
|
|
2
2
|
// server/public is served).
|
|
3
3
|
const CLAUDE_LONG_CONTEXT_LIMIT_TOKENS = 1000000;
|
|
4
|
+
// Mirrors shared/deepseek-models.mjs for the same reason.
|
|
5
|
+
const DEEPSEEK_CONTEXT_WINDOW_TOKENS = 1048576;
|
|
4
6
|
export const UNKNOWN_WINDOW_LABEL = '—';
|
|
5
7
|
|
|
6
8
|
// Shared by the context chip and both model pickers so a window reads the same
|
|
@@ -59,6 +61,8 @@ export function buildContextTierOptions({
|
|
|
59
61
|
// metadata either (that number is Copilot's): one blank default, as for a
|
|
60
62
|
// Claude model whose window is unknown.
|
|
61
63
|
if (provider === 'claude-cloud') return [{ value: 'default', label: UNKNOWN_WINDOW_LABEL }];
|
|
64
|
+
// DeepSeek's models have one window, 1M: no tier to choose.
|
|
65
|
+
if (provider === 'deepseek') return [{ value: 'default', label: tokenLabel(DEEPSEEK_CONTEXT_WINDOW_TOKENS) }];
|
|
62
66
|
const claudeTierList = provider === 'claude' ? claudeTiersForModel(modelId, claudeTiers) : null;
|
|
63
67
|
if (claudeTierList) {
|
|
64
68
|
return claudeTierList
|
|
@@ -23,6 +23,10 @@ export function isConversationUsingGrokProvider(conversation = null) {
|
|
|
23
23
|
return resolveConversationProviderType(conversation) === 'grok';
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function isConversationUsingDeepSeekProvider(conversation = null) {
|
|
27
|
+
return resolveConversationProviderType(conversation) === 'deepseek';
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
// Its own provider, never a flavour of `claude`: the session runs in a sandbox
|
|
27
31
|
// at Anthropic, so nothing that is true of a local Claude worker (modes,
|
|
28
32
|
// effort, steering, a working directory) may be assumed for it.
|
|
@@ -39,6 +43,7 @@ const PROVIDER_NAMES = Object.freeze({
|
|
|
39
43
|
'claude-cloud': 'Claude Cloud',
|
|
40
44
|
cursor: 'Cursor',
|
|
41
45
|
grok: 'Grok',
|
|
46
|
+
deepseek: 'DeepSeek',
|
|
42
47
|
github: 'Copilot',
|
|
43
48
|
'github-copilot': 'Copilot',
|
|
44
49
|
});
|
|
@@ -76,6 +81,7 @@ const SESSION_LOCK_LABELS = {
|
|
|
76
81
|
claude: 'Claude SDK',
|
|
77
82
|
cursor: 'Cursor SDK',
|
|
78
83
|
grok: 'Grok',
|
|
84
|
+
deepseek: 'DeepSeek',
|
|
79
85
|
'claude-cloud': `${CLAUDE_CLOUD_MARKER} Claude Cloud`,
|
|
80
86
|
};
|
|
81
87
|
|
|
@@ -96,6 +102,7 @@ export function sessionLockProviderKey({ providerType = '', model = '' } = {}) {
|
|
|
96
102
|
if (normalized === 'claude-cloud') return 'claude-cloud';
|
|
97
103
|
if (normalized === 'cursor') return 'cursor';
|
|
98
104
|
if (normalized === 'grok' || normalized === 'xai' || normalized === 'xai-grok') return 'grok';
|
|
105
|
+
if (normalized === 'deepseek') return 'deepseek';
|
|
99
106
|
if (normalized === 'github' || normalized === 'github-copilot') return 'github';
|
|
100
107
|
return '';
|
|
101
108
|
}
|
|
@@ -3523,6 +3523,8 @@ function runRelayErrorCta(cta) {
|
|
|
3523
3523
|
}
|
|
3524
3524
|
if (cta === 'open-grok-settings') openSettingsModal('providers', 'grok');
|
|
3525
3525
|
if (cta === 'open-claude-settings') openSettingsModal('providers', 'claude');
|
|
3526
|
+
if (cta === 'open-deepseek-settings') openSettingsModal('providers', 'deepseek');
|
|
3527
|
+
if (cta === 'open-deepseek-top-up') window.open('https://platform.deepseek.com/top_up', '_blank', 'noopener');
|
|
3526
3528
|
}
|
|
3527
3529
|
|
|
3528
3530
|
function handleBubbleActionClick(event) {
|