Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/sessionParser/sdkSessionAdapter.ts
13406 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
/**
7
* SDK Session Adapter
8
*
9
* Thin conversion layer between the `@anthropic-ai/claude-agent-sdk` session APIs
10
* and our internal types (`IClaudeCodeSessionInfo`, `IClaudeCodeSession`, `StoredMessage`).
11
*
12
* The SDK provides:
13
* - `listSessions()` / `getSessionInfo()` → `SDKSessionInfo`
14
* - `getSessionMessages()` → `SessionMessage[]`
15
*
16
* This adapter converts those into the shapes consumed by `chatHistoryBuilder.ts`
17
* and `claudeChatSessionContentProvider.ts` without any JSONL parsing.
18
*/
19
20
import type { SDKSessionInfo, SessionMessage } from '@anthropic-ai/claude-agent-sdk';
21
import {
22
AssistantMessageContent,
23
IClaudeCodeSession,
24
IClaudeCodeSessionInfo,
25
ISubagentSession,
26
StoredMessage,
27
UserMessageContent,
28
vAssistantMessageContent,
29
vUserMessageContent,
30
} from './claudeSessionSchema';
31
32
// #region Label Helpers
33
34
/**
35
* Strips `<system-reminder>` tags and their content from a string.
36
* The SDK includes raw system-reminder blocks in `summary` and `firstPrompt` fields.
37
*/
38
function stripSystemReminders(text: string): string {
39
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, '').trim();
40
}
41
42
/**
43
* Computes a clean display label from SDK session info.
44
* Priority: customTitle > stripped summary > stripped firstPrompt > fallback.
45
*/
46
function computeSessionLabel(info: SDKSessionInfo): string {
47
if (info.customTitle) {
48
return info.customTitle;
49
}
50
51
const summary = info.summary ? stripSystemReminders(info.summary) : '';
52
if (summary) {
53
return truncateLabel(summary);
54
}
55
56
const firstPrompt = info.firstPrompt ? stripSystemReminders(info.firstPrompt) : '';
57
if (firstPrompt) {
58
return truncateLabel(firstPrompt);
59
}
60
61
return 'Claude Session';
62
}
63
64
const MAX_LABEL_LENGTH = 50;
65
66
function truncateLabel(text: string): string {
67
const singleLine = text.replace(/\s+/g, ' ');
68
if (singleLine.length <= MAX_LABEL_LENGTH) {
69
return singleLine;
70
}
71
return singleLine.slice(0, MAX_LABEL_LENGTH) + '…';
72
}
73
74
// #endregion
75
76
// #region SDKSessionInfo → IClaudeCodeSessionInfo
77
78
/**
79
* Converts an `SDKSessionInfo` (from `listSessions` / `getSessionInfo`) into
80
* the lightweight `IClaudeCodeSessionInfo` used by the session list UI.
81
*/
82
export function sdkSessionInfoToSessionInfo(
83
info: SDKSessionInfo,
84
folderName?: string,
85
): IClaudeCodeSessionInfo {
86
return {
87
id: info.sessionId,
88
label: computeSessionLabel(info),
89
created: info.createdAt ?? info.lastModified,
90
lastRequestEnded: info.lastModified,
91
folderName,
92
cwd: info.cwd,
93
gitBranch: info.gitBranch,
94
};
95
}
96
97
// #endregion
98
99
// #region SessionMessage → StoredMessage
100
101
/**
102
* Converts an array of `SessionMessage` (from `getSessionMessages`) into
103
* `StoredMessage[]` compatible with `chatHistoryBuilder.ts`.
104
*
105
* Uses existing validators (`vUserMessageContent`, `vAssistantMessageContent`)
106
* to narrow the `message: unknown` field into typed content.
107
*
108
* Messages that fail validation are silently skipped — this matches the parser
109
* behavior of ignoring malformed JSONL entries.
110
*/
111
export function sdkSessionMessagesToStoredMessages(
112
messages: readonly SessionMessage[],
113
): StoredMessage[] {
114
const result: StoredMessage[] = [];
115
116
for (const msg of messages) {
117
const stored = sdkSessionMessageToStoredMessage(msg);
118
if (stored) {
119
result.push(stored);
120
}
121
}
122
123
return result;
124
}
125
126
function sdkSessionMessageToStoredMessage(
127
msg: SessionMessage,
128
): StoredMessage | undefined {
129
if (msg.type === 'user') {
130
const validated = vUserMessageContent.validate(msg.message);
131
if (validated.error) {
132
return undefined;
133
}
134
return {
135
uuid: msg.uuid,
136
sessionId: msg.session_id,
137
timestamp: new Date(0),
138
parentUuid: null,
139
type: 'user',
140
message: validated.content as UserMessageContent,
141
};
142
}
143
144
if (msg.type === 'assistant') {
145
const validated = vAssistantMessageContent.validate(msg.message);
146
if (validated.error) {
147
return undefined;
148
}
149
return {
150
uuid: msg.uuid,
151
sessionId: msg.session_id,
152
timestamp: new Date(0),
153
parentUuid: null,
154
type: 'assistant',
155
message: validated.content as AssistantMessageContent,
156
};
157
}
158
159
return undefined;
160
}
161
162
// #endregion
163
164
// #region Subagent Session Building
165
166
function extractParentToolUseId(messages: readonly SessionMessage[]): string | undefined {
167
for (const msg of messages) {
168
if (msg.type !== 'assistant' || msg.message === null || typeof msg.message !== 'object') {
169
continue;
170
}
171
if ('parent_tool_use_id' in msg.message) {
172
const id = msg.message.parent_tool_use_id;
173
if (typeof id === 'string') {
174
return id;
175
}
176
}
177
}
178
return undefined;
179
}
180
181
/**
182
* Converts SDK `SessionMessage[]` (from `getSubagentMessages`) into an
183
* `ISubagentSession` for display in the chat history.
184
*
185
* Extracts `parent_tool_use_id` from the first assistant message that
186
* contains one, to link the subagent back to its spawning Agent tool_use
187
* in the parent session.
188
*/
189
export function sdkSubagentMessagesToSubagentSession(
190
agentId: string,
191
messages: readonly SessionMessage[],
192
): ISubagentSession | null {
193
const storedMessages = sdkSessionMessagesToStoredMessages(messages);
194
if (storedMessages.length === 0) {
195
return null;
196
}
197
198
return {
199
agentId,
200
parentToolUseId: extractParentToolUseId(messages),
201
messages: storedMessages,
202
timestamp: storedMessages[storedMessages.length - 1].timestamp,
203
};
204
}
205
206
// #endregion
207
208
// #region Full Session Assembly
209
210
/**
211
* Assembles a full `IClaudeCodeSession` from SDK data and separately-loaded subagents.
212
*/
213
export function buildClaudeCodeSession(
214
info: SDKSessionInfo,
215
messages: readonly SessionMessage[],
216
subagents: readonly ISubagentSession[],
217
folderName?: string,
218
): IClaudeCodeSession {
219
const sessionInfo = sdkSessionInfoToSessionInfo(info, folderName);
220
const storedMessages = sdkSessionMessagesToStoredMessages(messages);
221
222
return {
223
...sessionInfo,
224
messages: storedMessages,
225
subagents,
226
};
227
}
228
229
// #endregion
230
231