| 1287 | } |
| 1288 | |
| 1289 | async createSession(config: SessionConfig): Promise<CopilotSession> { |
| 1290 | if (!this.connection) { |
| 1291 | await this.start(); |
| 1292 | } |
| 1293 | |
| 1294 | config = { ...this.configDefaultsForMode(), ...config }; |
| 1295 | config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage); |
| 1296 | |
| 1297 | // For cloud sessions, let the CLI/server assign the session id and |
| 1298 | // register the session lazily once the response arrives. For non-cloud |
| 1299 | // sessions we generate the id client-side (when the caller didn't |
| 1300 | // supply one) so the session can be registered BEFORE the RPC — the |
| 1301 | // CLI may issue session-scoped requests (e.g. `sessionFs.writeFile` |
| 1302 | // for workspace metadata) during `session.create` processing, before |
| 1303 | // it has sent the response. |
| 1304 | const callerSessionId = config.sessionId; |
| 1305 | const useServerGeneratedId = config.cloud != null && callerSessionId == null; |
| 1306 | const localSessionId = useServerGeneratedId ? undefined : (callerSessionId ?? randomUUID()); |
| 1307 | |
| 1308 | // Strip non-serializable bearerTokenProvider callbacks from provider configs, |
| 1309 | // replacing them with a wire flag; keep the callbacks for session-side |
| 1310 | // registration so the runtime can call back to acquire tokens. |
| 1311 | const { |
| 1312 | wireProvider: bearerWireProvider, |
| 1313 | wireProviders: bearerWireProviders, |
| 1314 | callbacks: bearerTokenCallbacks, |
| 1315 | } = extractBearerTokenProviders(config.provider, config.providers); |
| 1316 | |
| 1317 | // Extract transform callbacks from system message config before serialization. |
| 1318 | const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks( |
| 1319 | config.systemMessage |
| 1320 | ); |
| 1321 | |
| 1322 | // Creates the session object, wires up handlers, and registers it in |
| 1323 | // the sessions map. |
| 1324 | const initializeSession = (sessionId: string): CopilotSession => { |
| 1325 | const s = new CopilotSession( |
| 1326 | sessionId, |
| 1327 | this.connection!, |
| 1328 | undefined, |
| 1329 | this.onGetTraceContext, |
| 1330 | { mcpAuthHandler: config.onMcpAuthRequest } |
| 1331 | ); |
| 1332 | s.registerTools(config.tools); |
| 1333 | s.registerCanvases(config.canvases); |
| 1334 | s.registerCommands(config.commands); |
| 1335 | if (bearerTokenCallbacks.size > 0) { |
| 1336 | s.registerBearerTokenProviders(bearerTokenCallbacks); |
| 1337 | } |
| 1338 | s.registerPermissionHandler(config.onPermissionRequest); |
| 1339 | if (config.onUserInputRequest) { |
| 1340 | s.registerUserInputHandler(config.onUserInputRequest); |
| 1341 | } |
| 1342 | if (config.onElicitationRequest) { |
| 1343 | s.registerElicitationHandler(config.onElicitationRequest); |
| 1344 | } |
| 1345 | if (config.onExitPlanModeRequest) { |
| 1346 | s.registerExitPlanModeHandler(config.onExitPlanModeRequest); |