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 { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js';
7
>
import { Disposable } from '../../../base/common/lifecycle.js';
8
>
import { URI } from '../../../base/common/uri.js';
9
>
import { ILogService } from '../../log/common/log.js';
10
>
import { ISessionDataService } from '../common/sessionDataService.js';
11
>
import { ActionType } from '../common/state/sessionActions.js';
12
>
import { isAhpChatChannel, isDefaultChatUri, type Turn, type URI as ProtocolURI } from '../common/state/sessionState.js';
13
>
import { buildConversationContext, renderResponseMarkdown, truncateMiddle } from '../common/agentHostConversationContext.js';
14
>
import { AgentHostStateManager } from './agentHostStateManager.js';
15
>
import { ICopilotApiService, type ICopilotUtilityChatMessage } from './shared/copilotApiService.js';
16
>
17
>
const MAX_TITLE_LENGTH = 200;
18
>
19
>
/**
20
>
* Soft upper bound, in characters, for the first-turn context fed to the
21
>
* utility model when refining a session title. Sized to stay well within the
22
>
* small model's context window while leaving room for the prompt scaffolding.
23
>
*/
24
>
const MAX_TITLE_CONTEXT_CHARS = 20000;
25
>
26
>
export interface IAgentHostSessionTitleControllerOptions {
27
>
readonly sessionDataService: ISessionDataService;
28
>
readonly getGitHubCopilotToken?: () => string | undefined;
29
>
readonly copilotApiService?: ICopilotApiService;
30
>
}
31
>
32
>
export class AgentHostSessionTitleController extends Disposable {
33
>
34
>
private readonly _titleGenerationCancellationSources = new Map<ProtocolURI, CancellationTokenSource>();
35
>
36
>
/**
37
>
* The most recent title this controller applied for a given session/chat
38
>
* key. Used to detect whether the title was changed (e.g. a manual
39
>
* `/rename` or user edit) since we last set it, so we never clobber a
40
>
* deliberate title with an auto-generated one.
41
>
*/
42
>
private readonly _lastAppliedTitle = new Map<ProtocolURI, string>();
43
>
44
>
/**
45
>
* Session/chat keys whose current title is a provisional placeholder set by
46
>
* {@link seedProvisionalTitle} (e.g. from a `!command`). Such a title does
47
>
* not describe the session's topic, so the first subsequent request that
48
>
* carries real intent replaces it with a generated title via
49
>
* {@link seedTitleFromFirstMessage}.
50
>
*/
51
>
private readonly _provisionalTitles = new Set<ProtocolURI>();
52
>
53
>
constructor(
54
private readonly _stateManager: AgentHostStateManager,
55
private readonly _options: IAgentHostSessionTitleControllerOptions,