Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/claudeSessionStateService.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
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 { EffortLevel, PermissionMode } from '@anthropic-ai/claude-agent-sdk';
7
import { CapturingToken } from '../../../../platform/requestLogger/common/capturingToken';
8
import type { TraceContext } from '../../../../platform/otel/common/otelService';
9
import { arrayEquals } from '../../../../util/vs/base/common/equals';
10
import { Emitter } from '../../../../util/vs/base/common/event';
11
import { Disposable } from '../../../../util/vs/base/common/lifecycle';
12
import type { ClaudeFolderInfo } from '../common/claudeFolderInfo';
13
import type { ParsedClaudeModelId } from '../common/claudeModelId';
14
import type { IClaudeSessionStateService, SessionState, SessionStateChangeEvent, UsageHandler } from '../common/claudeSessionStateService';
15
16
export class ClaudeSessionStateService extends Disposable implements IClaudeSessionStateService {
17
declare _serviceBrand: undefined;
18
19
private readonly _onDidChangeSessionState = this._register(new Emitter<SessionStateChangeEvent>());
20
readonly onDidChangeSessionState = this._onDidChangeSessionState.event;
21
22
// State for sessions (model and permission mode selections)
23
// TODO: What about expiration of state for old sessions?
24
// TODO: Refactor setters to use a single `updateSession(id, patch)` method or spread
25
// pattern (`{ ...existing, field: value }`) so that adding a new field to SessionState
26
// doesn't require touching every existing setter.
27
private readonly _sessionState = new Map<string, SessionState>();
28
29
constructor() {
30
super();
31
}
32
33
getModelIdForSession(sessionId: string): ParsedClaudeModelId | undefined {
34
const state = this._sessionState.get(sessionId);
35
return state?.modelId;
36
}
37
38
setModelIdForSession(sessionId: string, modelId: ParsedClaudeModelId | undefined): void {
39
const existing = this._sessionState.get(sessionId);
40
if (existing?.modelId === modelId) {
41
return;
42
}
43
this._sessionState.set(sessionId, {
44
modelId,
45
permissionMode: existing?.permissionMode ?? 'acceptEdits',
46
capturingToken: existing?.capturingToken,
47
folderInfo: existing?.folderInfo,
48
usageHandler: existing?.usageHandler,
49
reasoningEffort: existing?.reasoningEffort,
50
traceContext: existing?.traceContext,
51
});
52
this._onDidChangeSessionState.fire({ sessionId, modelId });
53
}
54
55
getPermissionModeForSession(sessionId: string): PermissionMode {
56
return this._sessionState.get(sessionId)?.permissionMode ?? 'acceptEdits';
57
}
58
59
setPermissionModeForSession(sessionId: string, mode: PermissionMode): void {
60
const existing = this._sessionState.get(sessionId);
61
if (existing?.permissionMode === mode) {
62
return;
63
}
64
this._sessionState.set(sessionId, {
65
modelId: existing?.modelId,
66
permissionMode: mode,
67
capturingToken: existing?.capturingToken,
68
folderInfo: existing?.folderInfo,
69
usageHandler: existing?.usageHandler,
70
reasoningEffort: existing?.reasoningEffort,
71
traceContext: existing?.traceContext,
72
});
73
this._onDidChangeSessionState.fire({ sessionId, permissionMode: mode });
74
}
75
76
getCapturingTokenForSession(sessionId: string): CapturingToken | undefined {
77
return this._sessionState.get(sessionId)?.capturingToken;
78
}
79
80
setCapturingTokenForSession(sessionId: string, token: CapturingToken | undefined): void {
81
const existing = this._sessionState.get(sessionId);
82
this._sessionState.set(sessionId, {
83
modelId: existing?.modelId,
84
permissionMode: existing?.permissionMode ?? 'acceptEdits',
85
capturingToken: token,
86
folderInfo: existing?.folderInfo,
87
usageHandler: existing?.usageHandler,
88
reasoningEffort: existing?.reasoningEffort,
89
traceContext: existing?.traceContext,
90
});
91
}
92
93
getFolderInfoForSession(sessionId: string): ClaudeFolderInfo | undefined {
94
return this._sessionState.get(sessionId)?.folderInfo;
95
}
96
97
setFolderInfoForSession(sessionId: string, folderInfo: ClaudeFolderInfo): void {
98
const existing = this._sessionState.get(sessionId);
99
if (existing?.folderInfo?.cwd === folderInfo.cwd && arrayEquals(existing?.folderInfo?.additionalDirectories ?? [], folderInfo.additionalDirectories)) {
100
return;
101
}
102
this._sessionState.set(sessionId, {
103
modelId: existing?.modelId,
104
permissionMode: existing?.permissionMode ?? 'acceptEdits',
105
capturingToken: existing?.capturingToken,
106
folderInfo,
107
usageHandler: existing?.usageHandler,
108
reasoningEffort: existing?.reasoningEffort,
109
traceContext: existing?.traceContext,
110
});
111
this._onDidChangeSessionState.fire({ sessionId, folderInfo });
112
}
113
114
getUsageHandlerForSession(sessionId: string): UsageHandler | undefined {
115
return this._sessionState.get(sessionId)?.usageHandler;
116
}
117
118
setUsageHandlerForSession(sessionId: string, handler: UsageHandler | undefined): void {
119
const existing = this._sessionState.get(sessionId);
120
this._sessionState.set(sessionId, {
121
modelId: existing?.modelId,
122
permissionMode: existing?.permissionMode ?? 'acceptEdits',
123
capturingToken: existing?.capturingToken,
124
folderInfo: existing?.folderInfo,
125
usageHandler: handler,
126
reasoningEffort: existing?.reasoningEffort,
127
traceContext: existing?.traceContext,
128
});
129
}
130
131
getReasoningEffortForSession(sessionId: string): EffortLevel | undefined {
132
return this._sessionState.get(sessionId)?.reasoningEffort;
133
}
134
135
setReasoningEffortForSession(sessionId: string, effort: EffortLevel | undefined): void {
136
const existing = this._sessionState.get(sessionId);
137
if (existing?.reasoningEffort === effort) {
138
return;
139
}
140
this._sessionState.set(sessionId, {
141
modelId: existing?.modelId,
142
permissionMode: existing?.permissionMode ?? 'acceptEdits',
143
capturingToken: existing?.capturingToken,
144
folderInfo: existing?.folderInfo,
145
usageHandler: existing?.usageHandler,
146
reasoningEffort: effort,
147
traceContext: existing?.traceContext,
148
});
149
}
150
151
getTraceContextForSession(sessionId: string): TraceContext | undefined {
152
return this._sessionState.get(sessionId)?.traceContext;
153
}
154
155
setTraceContextForSession(sessionId: string, traceContext: TraceContext | undefined): void {
156
const existing = this._sessionState.get(sessionId);
157
this._sessionState.set(sessionId, {
158
modelId: existing?.modelId,
159
permissionMode: existing?.permissionMode ?? 'acceptEdits',
160
capturingToken: existing?.capturingToken,
161
folderInfo: existing?.folderInfo,
162
usageHandler: existing?.usageHandler,
163
reasoningEffort: existing?.reasoningEffort,
164
traceContext,
165
});
166
}
167
168
override dispose(): void {
169
this._sessionState.clear();
170
super.dispose();
171
}
172
}
173
174