1
>
/*---------------------------------------------------------------------------------------------
copilotSessionLauncher.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import type { CopilotClient, ExitPlanModeRequest, ExitPlanModeResult, NamedProviderConfig, PermissionRequestResult, ProviderModelConfig, ResumeSessionConfig, SessionConfig, Tool } from '@github/copilot-sdk';
7
>
import { coalesce } from '../../../../base/common/arrays.js';
8
>
import { Schemas } from '../../../../base/common/network.js';
9
>
import { URI } from '../../../../base/common/uri.js';
10
>
import { IFileService } from '../../../files/common/files.js';
11
>
import { ILogService, LogLevel } from '../../../log/common/log.js';
12
>
import { CopilotCliConfigKey, applyModelFamilyAlias, copilotCliConfigSchema } from '../../common/copilotCliConfig.js';
13
>
import { agentHostModelSupportsToolSearch, CLIENT_TOOL_SEARCH_REFERENCE_NAME } from './toolSearchDeferral.js';
14
>
import { AgentHostSessionSyncEnabledConfigKey, platformRootSchema, type AgentHostMcpServers } from '../../common/agentHostSchema.js';
15
>
import { AgentHostSandboxConfigKey, sandboxConfigSchema } from '../../common/sandboxConfigSchema.js';
16
>
import { IAgentConfigurationService } from '../agentConfigurationService.js';
17
>
import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js';
18
>
import { IByokLmBridgeRegistry } from '../byokLmBridgeRegistry.js';
19
>
import { IByokLmProxyService, type IByokLmProxyHandle } from './byokLmProxyService.js';
20
>
import type { IByokLmModelInfo } from '../../common/agentHostByokLm.js';
21
>
import type { ModelSelection, ToolDefinition } from '../../common/state/protocol/state.js';
22
>
import type { ActiveClientToolSet } from '../activeClientState.js';
23
>
import { CopilotSessionWrapper } from './copilotSessionWrapper.js';
24
>
import { ShellManager, createShellTools, type IUnsandboxedCommandConfirmationRequest } from './copilotShellTools.js';
25
>
import { toSdkHooks, toSdkInstructionDirectories, toSdkMcpServers, toSdkMcpServersFromConfigMap, toSdkSessionCustomAgents, toSdkSkillDirectories } from './copilotPluginConverters.js';
26
>
import { buildSandboxConfigForSdk, type ISdkSandboxConfig } from './sandboxConfigForSdk.js';
27
>
import type { ITypedPermissionRequest } from './copilotToolDisplay.js';
28
>
import type { ICopilotPluginInfo } from './copilotAgent.js';
29
>
import { agentHostPromptRegistry, type IAgentHostPromptContext } from './prompts/promptRegistry.js';
30
>
import { describeSystemMessageConfig } from './prompts/systemMessage.js';
31
>
import './prompts/allPrompts.js';
32
>
import { StopWatch } from '../../../../base/common/stopwatch.js';
33
>
34
>
export const ThinkingLevelConfigKey = 'thinkingLevel';
35
>
/**
36
>
* Config key for the numeric "Context Size" selection (a context-window token count). Mapped to the
37
>
* SDK's two-valued {@link SessionConfig.contextTier} by {@link getCopilotContextTier}.
38
>
*/
39
>
export const ContextSizeConfigKey = 'contextSize';
40
>
/**
41
>
* @deprecated Legacy config key that stored the resolved tier string (`'default'` / `'long_context'`)
42
>
* directly. Replaced by the numeric {@link ContextSizeConfigKey}; still read from persisted sessions
43
>
* for backward compatibility.
44
>
*/
45
>
export const ContextTierConfigKey = 'contextTier';
46
>
47
>
const ReasoningEfforts = ['low', 'medium', 'high', 'xhigh'] as const;
48
>
type ReasoningEffort = NonNullable<SessionConfig['reasoningEffort']>;
49
>
50
>
const ContextTiers = ['default', 'long_context'] as const;
51
>
type ContextTier = NonNullable<SessionConfig['contextTier']>;
52
>
const AGENT_HOST_COPILOT_CLIENT_NAME = 'vscode-agent-host';
53
>
54
>
type UserInputHandler = NonNullable<SessionConfig['onUserInputRequest']>;
55
>
type UserInputRequest = Parameters<UserInputHandler>[0];
56
>
type UserInputInvocation = Parameters<UserInputHandler>[1];
57
>
type UserInputResponse = Awaited<ReturnType<UserInputHandler>>;
58
>
type ElicitationHandler = NonNullable<SessionConfig['onElicitationRequest']>;
59
>
type ElicitationContext = Parameters<ElicitationHandler>[0];
60
>
type ElicitationResult = Awaited<ReturnType<ElicitationHandler>>;
61
>
type McpAuthHandler = NonNullable<SessionConfig['onMcpAuthRequest']>;
62
>
type McpAuthRequest = Parameters<McpAuthHandler>[0];
63
>
type McpAuthContext = Parameters<McpAuthHandler>[1];
64
>
type McpAuthResponse = Awaited<ReturnType<McpAuthHandler>>;
65
>
type SessionHooks = NonNullable<SessionConfig['hooks']>;
66
>
type PreToolUseHookInput = Parameters<NonNullable<SessionHooks['onPreToolUse']>>[0];
67
>
type PostToolUseHookInput = Parameters<NonNullable<SessionHooks['onPostToolUse']>>[0];
68
>
type CopilotSessionLaunchConfig = ResumeSessionConfig & {
69
>
readonly pluginDirectories?: string[];
70
>
readonly remoteSession?: 'export';
71
>
};
72
>
73
>
/**
74
>
* Immutable snapshot of the active client's structural contributions at
75
>
* session creation time. Used to detect when the session needs to be
76
>
* refreshed. Root MCP servers participate in restart detection because they
77
>
* are merged into the SDK session config. The owning `clientId`s are
78
>
* deliberately NOT part of this snapshot: client identity is tracked live via
79
>
* {@link ActiveClientToolSet} so a window
80
>
* reload (new `clientId`, identical tools/plugins) does not force a restart.
81
>
*/
82
>
export interface IActiveClientSnapshot {
83
>
readonly tools: readonly ToolDefinition[];
84
>
readonly plugins: readonly ICopilotPluginInfo[];
85
>
readonly mcpServers: AgentHostMcpServers;
86
>
}
87
>
88
>
/**
89
>
* The set of client-tool names the agent sees for a snapshot — each tool's
90
>
* `ToolDefinition.name` (the camelCase `toolReferenceName`). Used both to gate
91
>
* tool-specific prompt sections at launch and to route client tool calls during
92
>
* the session, so the two stay derived from one definition.
93
>
*/
94
>
export function clientToolNamesFromSnapshot(snapshot: IActiveClientSnapshot): ReadonlySet<string> {
95
return new Set(snapshot.tools.map(tool => tool.name));
96
}
98
>
export interface ICopilotSessionRuntime {
99
>
handlePermissionRequest(request: ITypedPermissionRequest): Promise<PermissionRequestResult>;
100
>
handleExitPlanModeRequest(request: ExitPlanModeRequest, invocation: { sessionId: string }): Promise<ExitPlanModeResult>;
101
>
handleUserInputRequest(request: UserInputRequest, invocation: UserInputInvocation): Promise<UserInputResponse>;
102
>
handleElicitationRequest(context: ElicitationContext): Promise<ElicitationResult>;
103
>
handleMcpAuthRequest(request: McpAuthRequest, context: McpAuthContext): Promise<McpAuthResponse>;
104
>
requestUnsandboxedCommandConfirmation(request: IUnsandboxedCommandConfirmationRequest): Promise<boolean>;
105
>
handlePreToolUse(input: PreToolUseHookInput): Promise<void>;
106
>
handlePostToolUse(input: PostToolUseHookInput): Promise<void>;
107
>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108
>
createClientSdkTools(): Tool<any>[];
109
>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
110
>
createServerSdkTools(): Tool<any>[];
111
>
}
112
>
113
>
export interface ICopilotSessionLauncher {
114
>
/**
115
>
* Creates an unowned SDK session wrapper. The caller is responsible for
116
>
* registering or disposing the returned wrapper.
117
>
*/
118
>
launch(plan: CopilotSessionLaunchPlan, runtime: ICopilotSessionRuntime): Promise<CopilotSessionWrapper>;
119
>
}
120
>
121
>
type CopilotSessionClient = Pick<CopilotClient, 'createSession' | 'resumeSession'>;
122
>
123
>
interface ICopilotSessionLaunchBase {
124
>
readonly client: CopilotSessionClient;
125
>
readonly sessionId: string;
126
>
readonly workingDirectory: URI | undefined;
127
>
readonly resolvedAgentName: string | undefined;
128
>
readonly snapshot: IActiveClientSnapshot;
129
>
/**
130
>
* Live, long-lived registry of every active client's tool contributions.
131
>
* Read at tool-call stamp time so a window reload (new `clientId`,
132
>
* identical tools) stamps subsequent client tool calls with the current
133
>
* owning id rather than the one frozen into {@link snapshot} at creation,
134
>
* and so a tool call is attributed to whichever client contributed it.
135
>
*/
136
>
readonly activeClientToolSet: ActiveClientToolSet;
137
>
readonly shellManager: ShellManager | undefined;
138
>
readonly githubToken: string | undefined;
139
>
140
>
/**
141
>
* Whether this is a workspace-less session. Threaded into the
142
>
* prompt context so the resolved system message gets the scratch/repoless
143
>
* variant. Named to match the `workspaceless` marker used throughout the AH
144
>
* layer (session `_meta`, stored metadata) that this value flows from.
145
>
*/
146
>
readonly workspaceless?: boolean;
147
>
}
148
>
149
>
export interface ICopilotCreateSessionLaunchPlan extends ICopilotSessionLaunchBase {
150
>
readonly kind: 'create';
151
>
readonly model: ModelSelection | undefined;
152
>
readonly longContextWindow?: number;
153
>
readonly freeLongContext?: boolean;
154
>
}
155
>
156
>
export interface ICopilotResumeSessionLaunchPlan extends ICopilotSessionLaunchBase {
157
>
readonly kind: 'resume';
158
>
readonly workingDirectory: URI;
159
>
readonly fallback: {
160
>
readonly model: ModelSelection | undefined;
161
>
readonly longContextWindow?: number;
162
>
readonly freeLongContext?: boolean;
163
>
};
164
>
}
165
>
166
>
export type CopilotSessionLaunchPlan = ICopilotCreateSessionLaunchPlan | ICopilotResumeSessionLaunchPlan;
167
>
168
function isReasoningEffort(value: unknown): value is ReasoningEffort {
169
return ReasoningEfforts.some(reasoningEffort => reasoningEffort === value);
170
}
172
function isContextTier(value: unknown): value is ContextTier {
173
return ContextTiers.some(contextTier => contextTier === value);
174
}
176
function getCopilotSdkErrorCode(err: unknown): number | undefined {
177
if (typeof err !== 'object' || err === null) {