541
shellTools = await createShellTools(plan.shellManager, this._terminalManager, this._logService, request => runtime.requestUnsandboxedCommandConfirmation(request));
542
}
544
>
// instead of feeding them explicitly, to avoid duplicates. Custom agents are the
545
>
// exception: the SDK validates the session-start `agent:` against `customAgents`
546
>
// by name, so the selected agent is force-included (see `toSdkSessionCustomAgents`).
547
>
const pluginsWithoutDirs = plugins.filter(p => !p.pluginDir || p.pluginDir.scheme !== Schemas.file);
548
>
const customAgents = await toSdkSessionCustomAgents(plugins, plan.resolvedAgentName, this._fileService);
549
>
const skillDirectories = toSdkSkillDirectories(pluginsWithoutDirs.flatMap(p => p.skills));
550
>
const instructionDirectories = toSdkInstructionDirectories(plugins.flatMap(p => p.instructions));
551
>
const model = plan.kind === 'create' ? plan.model : plan.fallback.model;
552
>
const clientToolNames = clientToolNamesFromSnapshot(plan.snapshot);
553
>
// Prompt routing and capability decisions use the family-aliased
554
>
// selection; the wire model id in _createSession comes from plan.model
555
>
// and is unaffected.
556
>
const effectiveModel = applyModelFamilyAlias(model, this._configurationService.getRootValue(copilotCliConfigSchema, CopilotCliConfigKey.ModelCapabilityOverrides));
557
>
if (model && effectiveModel !== model) {
558
this._logService.info(`[Copilot:${plan.sessionId}] Model capability override: routing prompt for '${model.id}' as family '${effectiveModel?.id}'`);
559
}
560
>
const toolSearchActive = this._configurationService.getRootValue(copilotCliConfigSchema, CopilotCliConfigKey.ToolSearchEnabled) === true
copilotSessionLauncher.ts
561
&& agentHostModelSupportsToolSearch(effectiveModel?.id)
562
&& clientToolNames.has(CLIENT_TOOL_SEARCH_REFERENCE_NAME);
564
>
getSetting: key => this._configurationService.getRootValue(copilotCliConfigSchema, key),
565
>
hasClientTool: name => clientToolNames.has(name),
566
>
workspaceless: plan.workspaceless === true,
567
>
toolSearchActive,
568
>
};
569
>
// Resolved once per (re)launch — the SDK has no mid-session system-message
570
>
// update, so this reflects the model/tools/settings at launch time. Log a
571
>
// summary at info for prompt observability; the full config at trace.
572
>
const systemMessage = agentHostPromptRegistry.resolveSystemMessageConfig(effectiveModel, promptContext);
573
>
this._logService.info(`[Copilot:${plan.sessionId}] Resolved system message: ${describeSystemMessageConfig(systemMessage)}`);
574
>
if (this._logService.getLevel() <= LogLevel.Trace) {
575
// Guarded: a `replace`-mode prompt's content can be multiple KB, so only
576
// serialize it when trace output is actually emitted.
577
this._logService.trace(`[Copilot:${plan.sessionId}] System message config: ${JSON.stringify(systemMessage, (_key, value) => typeof value === 'function' ? '[transform fn]' : value)}`);
578
}
580
>
...byok,
581
>
clientName: AGENT_HOST_COPILOT_CLIENT_NAME,
582
>
enableMcpApps: true,
583
>
enableFileHooks: true,
584
>
enableConfigDiscovery: true,
585
>
requestExtensions: false, // force-disable copilot extension management tools (otherwise enabled in experimental mode)
586
>
onPermissionRequest: request => runtime.handlePermissionRequest(request),
587
>
onUserInputRequest: (request, invocation) => runtime.handleUserInputRequest(request, invocation),
588
>
onElicitationRequest: context => runtime.handleElicitationRequest(context),
589
>
onMcpAuthRequest: (request, context) => runtime.handleMcpAuthRequest(request, context),
590
>
hooks: toSdkHooks(pluginsWithoutDirs.flatMap(p => p.hooks), {
591
>
onPreToolUse: input => runtime.handlePreToolUse(input),
592
>
onPostToolUse: input => runtime.handlePostToolUse(input),
593
>
}),
594
>
mcpServers: { ...toSdkMcpServersFromConfigMap(plan.snapshot.mcpServers), ...toSdkMcpServers(pluginsWithoutDirs.flatMap(p => p.mcpServers)) },
595
>
onExitPlanModeRequest: (request, invocation) => runtime.handleExitPlanModeRequest(request, invocation),
596
>
workingDirectory: plan.workingDirectory?.fsPath,
597
>
customAgents,
598
>
agent: plan.resolvedAgentName,
599
>
skillDirectories,
600
>
instructionDirectories,
601
>
systemMessage,
602
>
toolSearch: toolSearchActive ? { enabled: true, deferThreshold: 1 } : { enabled: false },
603
>
pluginDirectories: coalesce(plugins.map(p => p.pluginDir))
604
>
.filter(d => d.scheme === Schemas.file).map(d => d.fsPath),
605
>
tools: [...shellTools, ...runtime.createClientSdkTools(), ...runtime.createServerSdkTools()],
606
>
// Pass the GitHub token at the session level. The SDK's
607
>
// client-level `gitHubToken` authenticates the CLI process,
608
>
// but each session also needs its own token resolved into a
609
>
// GitHub identity (login, Copilot plan, endpoints) to drive
610
>
// model routing and quota — without this the session
611
>
// errors with "Session was not created with authentication
612
>
// info or custom provider" on first send. See #318693.
613
>
gitHubToken: plan.githubToken,
614
>
// Enable infinite sessions so the SDK provisions a workspace
615
>
// directory (containing `plan.md`, `checkpoints/`, `files/`).
616
>
// The workspace is required for plan mode to work — without
617
>
// it, `rpc.plan.read()` returns `path: null` and the SDK
618
>
// never emits `exit_plan_mode.requested`.
619
>
infiniteSessions: { enabled: true },
620
>
// Per-session remote export: the client-level `--remote` flag
621
>
// (enableRemoteSessions) enables the CLI capability, but each
622
>
// session must opt in via `remoteSession` to actually export
623
>
// events. Without this, sessions default to "off".
624
>
remoteSession: this._configurationService.getRootValue(platformRootSchema, AgentHostSessionSyncEnabledConfigKey) === true ? 'export' : undefined,
625
>
enableManagedSettings: true,
626
>
};
627
>
}
628
}