@@ -41,6 +41,7 @@ server without a new user message and reconnect to the same progressed session.
4141 dispatch(channel: string, action: SessionAction | ChatAction | TerminalAction | ClientChangesetAction | ClientAnnotationsAction | ClientAutomationAction | ClientAutomationRunAction | IRootConfigChangedAction): void {
4242 const seq = this._subscriptionManager.dispatchOptimistic(channel, action);
4343 this.dispatchAction(channel, action, this._clientId, seq);
44+
4445--- code-server.orig/lib/vscode/src/vs/platform/agentHost/common/agentHostByokLm.ts
4546+++ code-server/lib/vscode/src/vs/platform/agentHost/common/agentHostByokLm.ts
4647@@ -135,6 +135,17 @@
@@ -61,7 +62,13 @@ server without a new user message and reconnect to the same progressed session.
6162 export interface IByokLmModelInfo {
6263 /** Provider/vendor name (the LM API vendor that registered the model). */
6364 readonly vendor: string;
64- @@ -161,6 +172,11 @@
65+ @@ -157,10 +168,17 @@
66+ readonly maxOutputTokens?: number;
67+ /** Whether the model accepts image inputs, when known. */
68+ readonly supportsVision?: boolean;
69+ + /** Whether the model supports canonical tool calling, when known. */
70+ + readonly supportsToolCalling?: boolean;
71+ /** Reasoning effort values advertised by the renderer model, when known. */
6572 readonly supportedReasoningEfforts?: readonly string[];
6673 /** Default reasoning effort advertised by the renderer model, when known. */
6774 readonly defaultReasoningEffort?: string;
@@ -73,7 +80,7 @@ server without a new user message and reconnect to the same progressed session.
7380 }
7481
7582 /**
76- @@ -177,7 +193 ,9 @@
83+ @@ -177,7 +195 ,9 @@
7784
7885 /** Returns the provider-qualified model id advertised by the agent host. */
7986 export function getByokLmAgentModelId(model: IByokLmModelInfo): string {
@@ -84,6 +91,7 @@ server without a new user message and reconnect to the same progressed session.
8491 }
8592
8693 /** Resolves BYOK enablement and trace context from synchronized root configuration. */
94+
8795--- code-server.orig/lib/vscode/src/vs/platform/agentHost/common/agentHostEnablementService.ts
8896+++ code-server/lib/vscode/src/vs/platform/agentHost/common/agentHostEnablementService.ts
8997@@ -51,9 +51,8 @@
@@ -108,6 +116,7 @@ server without a new user message and reconnect to the same progressed session.
108116 },
109117 'chat.editor.localAgent.enabled': {
110118 type: 'boolean',
119+
111120--- code-server.orig/lib/vscode/src/vs/platform/agentHost/common/agentHostStarter.config.contribution.ts
112121+++ code-server/lib/vscode/src/vs/platform/agentHost/common/agentHostStarter.config.contribution.ts
113122@@ -279,10 +279,9 @@
@@ -123,6 +132,7 @@ server without a new user message and reconnect to the same progressed session.
123132 },
124133 [AgentHostCodexAgentEnabledSettingId]: {
125134 type: 'boolean',
135+
126136--- code-server.orig/lib/vscode/src/vs/platform/agentHost/common/agentService.ts
127137+++ code-server/lib/vscode/src/vs/platform/agentHost/common/agentService.ts
128138@@ -13,6 +13,7 @@
@@ -155,7 +165,7 @@ server without a new user message and reconnect to the same progressed session.
155165+
156166 /** Restart the agent host process, if this connection owns its lifecycle. */
157167 restartAgentHost(): Promise<void>;
158-
168+
159169--- code-server.orig/lib/vscode/src/vs/platform/agentHost/common/ahpJsonlLogger.ts
160170+++ code-server/lib/vscode/src/vs/platform/agentHost/common/ahpJsonlLogger.ts
161171@@ -99,7 +99,7 @@
@@ -206,6 +216,7 @@ server without a new user message and reconnect to the same progressed session.
206216 export function getAhpLogByteLength(text: string): number {
207217 return VSBuffer.fromString(text).byteLength;
208218 }
219+
209220--- code-server.orig/lib/vscode/src/vs/platform/agentHost/node/agentHostServerMain.ts
210221+++ code-server/lib/vscode/src/vs/platform/agentHost/node/agentHostServerMain.ts
211222@@ -32,6 +32,7 @@
@@ -233,6 +244,7 @@ server without a new user message and reconnect to the same progressed session.
233244 });
234245 disposables.add(runtime);
235246 const { agentService, instantiationService } = runtime;
247+
236248--- code-server.orig/lib/vscode/src/vs/platform/agentHost/node/agentService.ts
237249+++ code-server/lib/vscode/src/vs/platform/agentHost/node/agentService.ts
238250@@ -80,6 +80,8 @@
@@ -271,6 +283,7 @@ server without a new user message and reconnect to the same progressed session.
271283 dispatchAction(channel: string, action: SessionAction | ChatAction | TerminalAction | ClientChangesetAction | ClientAnnotationsAction | IRootConfigChangedAction | ClientAutomationAction | ClientAutomationRunAction, clientId: string, clientSeq: number, clientContextOrType: IAgentHostClientTelemetryContext | AgentHostClientType = AgentHostClientType.Unknown): void {
272284 const clientContext = typeof clientContextOrType === 'string'
273285 ? createUnknownAgentHostClientTelemetryContext(clientContextOrType)
286+
274287--- code-server.orig/lib/vscode/src/vs/platform/agentHost/node/byokLmBridgeRegistry.ts
275288+++ code-server/lib/vscode/src/vs/platform/agentHost/node/byokLmBridgeRegistry.ts
276289@@ -38,6 +38,9 @@
@@ -315,15 +328,17 @@ server without a new user message and reconnect to the same progressed session.
315328 }
316329
317330 getServingConnection(): IByokLmBridgeConnection | undefined {
318- @@ -171,6 +189,7 @@
331+ @@ -170,7 +188,9 @@
332+ && m.maxPromptTokens === n.maxPromptTokens
319333 && m.maxOutputTokens === n.maxOutputTokens
320334 && m.supportsVision === n.supportsVision
335+ + && m.supportsToolCalling === n.supportsToolCalling
321336 && m.defaultReasoningEffort === n.defaultReasoningEffort
322337+ && JSON.stringify(m.directProvider) === JSON.stringify(n.directProvider)
323338 && arraysEqual(m.supportedReasoningEfforts, n.supportedReasoningEfforts);
324339 });
325340 }
326- @@ -192,6 +211 ,8 @@
341+ @@ -192,6 +212 ,8 @@
327342 return Disposable.None;
328343 }
329344
@@ -332,6 +347,7 @@ server without a new user message and reconnect to the same progressed session.
332347 getModels(): readonly IByokLmModelInfo[] {
333348 return [];
334349 }
350+
335351--- code-server.orig/lib/vscode/src/vs/platform/agentHost/node/copilot/copilotSessionLauncher.ts
336352+++ code-server/lib/vscode/src/vs/platform/agentHost/node/copilot/copilotSessionLauncher.ts
337353@@ -13,7 +13,7 @@
@@ -343,7 +359,7 @@ server without a new user message and reconnect to the same progressed session.
343359 import { AgentHostByokModelsEnabledConfigKey, AgentHostMcpConnectorsEnabledConfigKey, AgentHostSessionSyncEnabledConfigKey, platformRootSchema, type AgentHostMcpServers } from '../../common/agentHostSchema.js';
344360 import { CopilotCliConfigKey, copilotCliConfigSchema, normalizeModelFamilyAlias, normalizeToolSearchDeferThreshold, resolveModelCapabilityOverrideField } from '../../common/copilotCliConfig.js';
345361 import { IAgentHostOTelService } from '../../common/otel/agentHostOTelService.js';
346- @@ -543,66 +543,114 @@
362+ @@ -543,66 +543,117 @@
347363 * unit-testable without instantiating the launcher; the launcher passes a
348364 * `startProxy` thunk that memoizes the single shared proxy handle.
349365 */
@@ -352,6 +368,9 @@ server without a new user message and reconnect to the same progressed session.
352368+ if (model.supportsVision !== undefined) {
353369+ supports.vision = model.supportsVision;
354370+ }
371+ + if (model.supportsToolCalling !== undefined) {
372+ + supports.toolCalls = model.supportsToolCalling;
373+ + }
355374+ if (model.supportedReasoningEfforts?.length) {
356375+ supports.reasoningEffort = true;
357376+ }
@@ -495,6 +514,7 @@ server without a new user message and reconnect to the same progressed session.
495514 }
496515
497516 /** Applies sandbox configuration, returning false when the runtime retains its policy after a managed conflict. */
517+
498518--- code-server.orig/lib/vscode/src/vs/platform/agentHost/node/protocolServerHandler.ts
499519+++ code-server/lib/vscode/src/vs/platform/agentHost/node/protocolServerHandler.ts
500520@@ -18,6 +18,7 @@
@@ -505,7 +525,7 @@ server without a new user message and reconnect to the same progressed session.
505525 import { isManagedSettingsPermissions } from '../common/agentHostManagedSettings.js';
506526 import { isAnnotationsUri } from '../common/annotationsUri.js';
507527 import { parseChangesetUri } from '../common/changesetUri.js';
508- @@ -145,6 +146,81 @@
528+ @@ -145,6 +146,82 @@
509529 return channel;
510530 }
511531
@@ -514,7 +534,7 @@ server without a new user message and reconnect to the same progressed session.
514534+ const MAX_EPHEMERAL_BYOK_REASONING_EFFORTS = 64;
515535+ const EPHEMERAL_BYOK_MODEL_KEYS = new Set([
516536+ 'vendor', 'id', 'name', 'modelIdentifier', 'maxContextWindowTokens', 'maxPromptTokens', 'maxOutputTokens',
517- + 'supportsVision', 'supportedReasoningEfforts', 'defaultReasoningEffort', 'directProvider',
537+ + 'supportsVision', 'supportsToolCalling', ' supportedReasoningEfforts', 'defaultReasoningEffort', 'directProvider',
518538+ ]);
519539+ const EPHEMERAL_BYOK_PROVIDER_KEYS = new Set(['name', 'type', 'wireApi', 'baseUrl', 'apiKey', 'modelId']);
520540+
@@ -559,6 +579,7 @@ server without a new user message and reconnect to the same progressed session.
559579+ || !isOptionalTokenCount(candidate.maxPromptTokens)
560580+ || !isOptionalTokenCount(candidate.maxOutputTokens)
561581+ || !isOptionalBoolean(candidate.supportsVision)
582+ + || !isOptionalBoolean(candidate.supportsToolCalling)
562583+ || !isOptionalReasoningEfforts(candidate.supportedReasoningEfforts)
563584+ || !isBoundedString(candidate.defaultReasoningEffort, true)) {
564585+ return false;
@@ -587,7 +608,7 @@ server without a new user message and reconnect to the same progressed session.
587608 /**
588609 * Methods handled by the request dispatcher. Excludes `initialize`,
589610 * `reconnect`, and `ping`, which are handled directly during message
590- @@ -523,6 +599 ,21 @@
611+ @@ -523,6 +600 ,21 @@
591612 }
592613 return;
593614 }
@@ -609,6 +630,7 @@ server without a new user message and reconnect to the same progressed session.
609630 // Notification — fire-and-forget
610631 switch (msg.method) {
611632 case 'unsubscribe':
633+
612634--- code-server.orig/lib/vscode/src/vs/platform/agentHost/test/common/ahpJsonlLogger.test.ts
613635+++ code-server/lib/vscode/src/vs/platform/agentHost/test/common/ahpJsonlLogger.test.ts
614636@@ -114,6 +114,58 @@
@@ -670,6 +692,7 @@ server without a new user message and reconnect to the same progressed session.
670692 test('rotates JSONL files and keeps bounded history', async () => {
671693 const fileService = store.add(new FileService(new NullLogService()));
672694 store.add(fileService.registerProvider('file', store.add(new InMemoryFileSystemProvider())));
695+
673696--- code-server.orig/lib/vscode/src/vs/platform/agentHost/test/node/byokLmBridgeRegistry.test.ts
674697+++ code-server/lib/vscode/src/vs/platform/agentHost/test/node/byokLmBridgeRegistry.test.ts
675698@@ -59,6 +59,64 @@
@@ -737,9 +760,10 @@ server without a new user message and reconnect to the same progressed session.
737760 test('a window that pushes an empty list is still a valid serving target', () => {
738761 const registry = new ByokLmBridgeRegistry();
739762 const only = pushable();
763+
740764--- code-server.orig/lib/vscode/src/vs/platform/agentHost/test/node/copilotSessionLauncher.test.ts
741765+++ code-server/lib/vscode/src/vs/platform/agentHost/test/node/copilotSessionLauncher.test.ts
742- @@ -417,10 +417,52 @@
766+ @@ -417,10 +417,53 @@
743767 assert.strictEqual(proxy.starts, 0);
744768 });
745769
@@ -752,6 +776,7 @@ server without a new user message and reconnect to the same progressed session.
752776+ name: 'Fixture Model',
753777+ maxContextWindowTokens: 128000,
754778+ supportsVision: true,
779+ + supportsToolCalling: true,
755780+ supportedReasoningEfforts: ['low', 'high'],
756781+ directProvider: {
757782+ name: 'vscode-byok-customendpoint-abc',
@@ -780,7 +805,7 @@ server without a new user message and reconnect to the same progressed session.
780805+ provider: 'vscode-byok-customendpoint-abc',
781806+ name: 'Fixture Model',
782807+ maxContextWindowTokens: 128000,
783- + capabilities: { supports: { vision: true, reasoningEffort: true } },
808+ + capabilities: { supports: { vision: true, toolCalls: true, reasoningEffort: true } },
784809+ }],
785810+ });
786811+ });
@@ -793,7 +818,7 @@ server without a new user message and reconnect to the same progressed session.
793818 { vendor: 'acme', id: 'gpt', name: undefined, maxContextWindowTokens: undefined },
794819 { vendor: 'globex', id: 'llama', name: 'Globex Llama' },
795820 ]));
796- @@ -436,7 +478 ,7 @@
821+ @@ -436,7 +479 ,7 @@
797822 { name: 'globex', type: 'openai', wireApi: 'responses', baseUrl: 'http://127.0.0.1:1/v/globex', bearerToken: 'NONCE.sess-1' },
798823 ],
799824 models: [
@@ -802,6 +827,7 @@ server without a new user message and reconnect to the same progressed session.
802827 { id: 'gpt', provider: 'acme' },
803828 { id: 'llama', provider: 'globex', name: 'Globex Llama' },
804829 ],
830+
805831--- code-server.orig/lib/vscode/src/vs/platform/agentHost/test/node/protocolServerHandler.test.ts
806832+++ code-server/lib/vscode/src/vs/platform/agentHost/test/node/protocolServerHandler.test.ts
807833@@ -22,6 +22,7 @@
@@ -897,6 +923,7 @@ server without a new user message and reconnect to the same progressed session.
897923 test('setClientManagedSettingsPermissions validates and attributes contributions to the connected client', async () => {
898924 const transport = connectClient('client-managed-settings-contribution');
899925 transport.sent.length = 0;
926+
900927--- code-server.orig/lib/vscode/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHost.contribution.ts
901928+++ code-server/lib/vscode/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHost.contribution.ts
902929@@ -15,9 +15,11 @@
@@ -962,6 +989,7 @@ server without a new user message and reconnect to the same progressed session.
962989 registerWorkbenchContribution2(AgentHostExistingSessionHarnessPickerEnablement.ID, AgentHostExistingSessionHarnessPickerEnablement, WorkbenchPhase.BlockRestore);
963990 registerWorkbenchContribution2(AgentHostLegacyMigrationGateContribution.ID, AgentHostLegacyMigrationGateContribution, WorkbenchPhase.BlockStartup);
964991 registerWorkbenchContribution2(CopilotConfigSlashSubmitHandlerContribution.ID, CopilotConfigSlashSubmitHandlerContribution, WorkbenchPhase.AfterRestored);
992+
965993--- code-server.orig/lib/vscode/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostByokLmHandler.ts
966994+++ code-server/lib/vscode/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostByokLmHandler.ts
967995@@ -6,7 +6,7 @@
@@ -981,7 +1009,7 @@ server without a new user message and reconnect to the same progressed session.
9811009 IByokLmModelInfo,
9821010 IByokLmOutputItem,
9831011 IByokLmReasoningItem,
984- @@ -39,6 +40,115 @@
1012+ @@ -39,6 +40,118 @@
9851013 const VSCODE_REASONING_SUMMARY_PART_DONE = 'vscode_reasoning_summary_part_done';
9861014 const CLIENT_BYOK_CONTEXT_KEYS = new Set([ChatEntitlementContextKeys.clientByokEnabled.key]);
9871015
@@ -1029,10 +1057,13 @@ server without a new user message and reconnect to the same progressed session.
10291057+ }
10301058+ const modelOptions = asRecord(model.modelOptions);
10311059+ const requestHeaders = asRecord(model.requestHeaders);
1060+ + const editTools = Array.isArray(model.editTools) ? model.editTools : [];
10321061+ return !!(modelOptions && Object.keys(modelOptions).length > 0)
10331062+ || !!(requestHeaders && Object.keys(requestHeaders).length > 0)
10341063+ || model.zeroDataRetentionEnabled === true
10351064+ || model.adaptiveThinking === true
1065+ + || model.thinking === true
1066+ + || editTools.length > 0
10361067+ || typeof model.minThinkingBudget === 'number'
10371068+ || typeof model.maxThinkingBudget === 'number'
10381069+ || model.streaming === false
@@ -1097,7 +1128,7 @@ server without a new user message and reconnect to the same progressed session.
10971128 /**
10981129 * Renderer-side {@link IAgentHostByokLmHandler}. Services BYOK chat requests
10991130 * forwarded by the node agent host's OpenAI proxy by calling the VS Code LM
1100- @@ -169,6 +279 ,10 @@
1131+ @@ -169,6 +282 ,10 @@
11011132 // Only genuine renderer BYOK models — exclude agent-host copies, which
11021133 // carry a `targetChatSessionType` and would otherwise re-enter the bridge.
11031134 if (metadata?.isBYOK && !metadata.targetChatSessionType) {
@@ -1108,14 +1139,18 @@ server without a new user message and reconnect to the same progressed session.
11081139 const reasoningEffortSchema = metadata.configurationSchema?.properties?.reasoningEffort;
11091140 const supportedReasoningEfforts = reasoningEffortSchema?.enum?.filter((value): value is string => typeof value === 'string');
11101141 const defaultReasoningEffort = typeof reasoningEffortSchema?.default === 'string' ? reasoningEffortSchema.default : undefined;
1111- @@ -183,6 +297,7 @@
1142+ @@ -181,8 +298,10 @@
1143+ maxPromptTokens: metadata.maxInputTokens,
1144+ maxOutputTokens: metadata.maxOutputTokens,
11121145 supportsVision: !!metadata.capabilities?.vision,
1146+ + ...(metadata.capabilities?.toolCalling !== undefined ? { supportsToolCalling: metadata.capabilities.toolCalling } : {}),
11131147 ...(supportedReasoningEfforts?.length ? { supportedReasoningEfforts } : {}),
11141148 ...(defaultReasoningEffort !== undefined ? { defaultReasoningEffort } : {}),
11151149+ ...(directProvider ? { directProvider } : {}),
11161150 };
11171151 const agentHostModelIdentifier = `${SessionType.AgentHostCopilot}:${getByokLmAgentModelId(model)}`;
11181152 if (!this._languageModelsService.isModelHidden(identifier) && !this._languageModelsService.isModelHidden(agentHostModelIdentifier)) {
1153+
11191154--- code-server.orig/lib/vscode/src/vs/workbench/contrib/chat/common/languageModels.ts
11201155+++ code-server/lib/vscode/src/vs/workbench/contrib/chat/common/languageModels.ts
11211156@@ -560,6 +560,12 @@
@@ -1161,6 +1196,7 @@ server without a new user message and reconnect to the same progressed session.
11611196 hasResolvedVendor(vendor: string): boolean {
11621197 return this._modelsGroups.has(vendor);
11631198 }
1199+
11641200--- code-server.orig/lib/vscode/src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostByokLmHandler.test.ts
11651201+++ code-server/lib/vscode/src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostByokLmHandler.test.ts
11661202@@ -18,7 +18,7 @@
@@ -1191,15 +1227,15 @@ server without a new user message and reconnect to the same progressed session.
11911227 override isModelHidden(identifier: string): boolean {
11921228 return this._isModelHidden(identifier);
11931229 }
1194- @@ -197,6 +202,163 @@
1230+ @@ -197,6 +202,166 @@
11951231 ]);
11961232 });
11971233
11981234+
11991235+ test('listModels attaches an ephemeral direct OpenAI-compatible provider config', async () => {
12001236+ const identifier = 'customendpoint/Test Group/fixture-model';
12011237+ const service = new TestLanguageModelsService(
1202- + new Map([[identifier, byokModel('customendpoint', 'fixture-model')]]),
1238+ + new Map([[identifier, byokModel('customendpoint', 'fixture-model', { toolCalling: true } )]]),
12031239+ () => responseOf([]),
12041240+ Event.None,
12051241+ () => false,
@@ -1225,6 +1261,7 @@ server without a new user message and reconnect to the same progressed session.
12251261+
12261262+ assert.strictEqual(model.vendor, 'customendpoint');
12271263+ assert.strictEqual(model.id, 'fixture-model');
1264+ + assert.strictEqual(model.supportsToolCalling, true);
12281265+ assert.ok(model.directProvider?.name.startsWith('vscode-byok-customendpoint-'));
12291266+ assert.deepStrictEqual({ ...model.directProvider, name: '<stable-synthetic>' }, {
12301267+ name: '<stable-synthetic>',
@@ -1336,6 +1373,8 @@ server without a new user message and reconnect to the same progressed session.
13361373+ { name: 'modelOptions', extra: { modelOptions: { temperature: 0.4 } } },
13371374+ { name: 'zero data retention', extra: { zeroDataRetentionEnabled: true } },
13381375+ { name: 'adaptive thinking', extra: { adaptiveThinking: true } },
1376+ + { name: 'thinking mode', extra: { thinking: true } },
1377+ + { name: 'edit tools', extra: { editTools: ['apply-patch'] } },
13391378+ { name: 'thinking budget', extra: { minThinkingBudget: 1024, maxThinkingBudget: 4096 } },
13401379+ { name: 'non-streaming', extra: { streaming: false } },
13411380+ { name: 'reasoning effort format', extra: { reasoningEffortFormat: 'responses' } },
@@ -1355,6 +1394,7 @@ server without a new user message and reconnect to the same progressed session.
13551394 test('listModels excludes hidden BYOK sources and Agent Host copies', async () => {
13561395 const sourceIdentifier = 'openrouter/OpenRouter 2/ai21/jamba-large-1.7';
13571396 const agentHostIdentifier = `${SessionType.AgentHostCopilot}:openrouter/OpenRouter 2/ai21/jamba-large-1.7`;
1397+
13581398--- code-server.orig/lib/vscode/src/vs/workbench/services/agentHost/browser/editorRemoteAgentHostServiceClient.ts
13591399+++ code-server/lib/vscode/src/vs/workbench/services/agentHost/browser/editorRemoteAgentHostServiceClient.ts
13601400@@ -21,6 +21,7 @@
0 commit comments