Skip to content

Commit fdb2524

Browse files
committed
feat(sessions): improve session title handling and tool message insertion
- Replace string comparison with ToolType enum in SessionManager for type safety - Use GetProjectWithProvider instead of GetProject for proper provider context - Add insertToolMessage function to correctly position tool messages after anchor points - Implement onFirstMessage callback to auto-generate session titles from first user message - Update SessionAwareProjectChat to handle first message and update session title - Add patch method mock to useSessionManager tests for API client compatibility - Improve tool message ordering by finding last consecutive tool message before insertion - Enhance session title generation by truncating first message to 50 characters
1 parent 09ca14d commit fdb2524

4 files changed

Lines changed: 61 additions & 6 deletions

File tree

src/MyYuCode/Services/Sessions/SessionManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public async Task UpdateSessionTitleAsync(Guid sessionId, string title)
158158
/// </summary>
159159
public async Task SyncProjectSessionTitlesAsync(Guid projectId)
160160
{
161-
var project = _dataStore.GetProject(projectId);
161+
var project = _dataStore.GetProjectWithProvider(projectId);
162162
if (project == null) return;
163163

164164
var sessions = _dataStore.GetSessionsByProject(projectId);
@@ -167,7 +167,7 @@ public async Task SyncProjectSessionTitlesAsync(Guid projectId)
167167
bool changed = false;
168168
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
169169

170-
if (string.Equals(project.ToolType, "Codex", StringComparison.OrdinalIgnoreCase))
170+
if (project.ToolType == ToolType.Codex)
171171
{
172172
var historyPath = Path.Combine(userHome, ".codex", "history.jsonl");
173173
if (File.Exists(historyPath))
@@ -232,7 +232,7 @@ public async Task SyncProjectSessionTitlesAsync(Guid projectId)
232232
}
233233
}
234234
}
235-
else if (string.Equals(project.ToolType, "ClaudeCode", StringComparison.OrdinalIgnoreCase))
235+
else if (project.ToolType == ToolType.ClaudeCode)
236236
{
237237
foreach (var session in sessions)
238238
{

web/src/components/project-workspace/ProjectChat.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,37 @@ function insertAfterMessage(
23112311
return next
23122312
}
23132313

2314+
// 插入 tool 消息时,找到最后一个 tool 消息或锚点消息,确保新 tool 在正确位置
2315+
function insertToolMessage(
2316+
prev: ChatMessage[],
2317+
anchorMessageId: string | null | undefined,
2318+
message: ChatMessage,
2319+
): ChatMessage[] {
2320+
// 先找锚点位置
2321+
let anchorIndex = anchorMessageId ? prev.findIndex((m) => m.id === anchorMessageId) : -1
2322+
2323+
// 从锚点位置向后找最后一个连续的 tool 消息
2324+
if (anchorIndex >= 0) {
2325+
let lastToolIndex = anchorIndex
2326+
for (let i = anchorIndex + 1; i < prev.length; i++) {
2327+
if (prev[i].kind === 'tool' && prev[i].role === 'agent') {
2328+
lastToolIndex = i
2329+
} else {
2330+
break
2331+
}
2332+
}
2333+
// 如果找到了 tool 消息,在最后一个 tool 后面插入
2334+
if (lastToolIndex > anchorIndex || (prev[anchorIndex]?.kind === 'tool' && prev[anchorIndex]?.role === 'agent')) {
2335+
const next = [...prev]
2336+
next.splice(lastToolIndex + 1, 0, message)
2337+
return next
2338+
}
2339+
}
2340+
2341+
// 否则使用默认的插入逻辑
2342+
return insertAfterMessage(prev, anchorMessageId, message)
2343+
}
2344+
23142345
export function ProjectChat({
23152346
project,
23162347
detailsOpen,
@@ -3543,6 +3574,14 @@ export function ProjectChat({
35433574
if (sending) return
35443575
if ((!text && readyImages.length === 0) || hasBlockingUploads) return
35453576

3577+
// 如果是第一条消息,通知父组件(用于设置会话标题)
3578+
const isFirstMessage = messages.length === 0
3579+
if (isFirstMessage && text && onFirstMessage) {
3580+
// 截取前50个字符作为标题
3581+
const title = text.length > 50 ? text.slice(0, 47) + '...' : text
3582+
onFirstMessage(title)
3583+
}
3584+
35463585
setChatError(null)
35473586
setDraft('')
35483587
setMentionToken(null)
@@ -3986,7 +4025,7 @@ export function ProjectChat({
39864025
return next
39874026
}
39884027
inserted = true
3989-
return insertAfterMessage(prev, anchorAfterId, toolMessage)
4028+
return insertToolMessage(prev, anchorAfterId, toolMessage)
39904029
})
39914030

39924031
if (inserted) {
@@ -4048,7 +4087,7 @@ export function ProjectChat({
40484087
}
40494088

40504089
inserted = true
4051-
return insertAfterMessage(prev, anchorAfterId, toolMessage)
4090+
return insertToolMessage(prev, anchorAfterId, toolMessage)
40524091
})
40534092

40544093
if (inserted) {
@@ -4142,7 +4181,7 @@ export function ProjectChat({
41424181
}
41434182

41444183
const anchorAfterId = streamTailMessageId
4145-
setMessages((prev) => insertAfterMessage(prev, anchorAfterId, toolMessage))
4184+
setMessages((prev) => insertToolMessage(prev, anchorAfterId, toolMessage))
41464185
toolChainTailMessageId = toolMessageId
41474186
markStreamTail('tool', toolMessageId)
41484187

@@ -4246,7 +4285,9 @@ export function ProjectChat({
42464285
apiBase,
42474286
draft,
42484287
draftImages,
4288+
messages.length,
42494289
modelSelection,
4290+
onFirstMessage,
42504291
onToolOutput,
42514292
project.id,
42524293
project.toolType,

web/src/components/project-workspace/SessionAwareProjectChat.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ export function SessionAwareProjectChat({
133133
}
134134
}, [updateSessionTitle]);
135135

136+
// Handle first message - update session title
137+
const handleFirstMessage = useCallback(async (text: string) => {
138+
if (currentSession?.id) {
139+
try {
140+
await updateSessionTitle(currentSession.id, text);
141+
} catch (error) {
142+
console.error('Failed to update session title:', error);
143+
}
144+
}
145+
}, [currentSession?.id, updateSessionTitle]);
146+
136147
// Toggle session panel
137148
const toggleSessionPanel = useCallback(() => {
138149
setSessionPanelOpen(prev => !prev);
@@ -212,6 +223,7 @@ export function SessionAwareProjectChat({
212223
onToolOutput={onToolOutput}
213224
currentToolType={currentToolType}
214225
sessionId={currentSession?.id}
226+
onFirstMessage={handleFirstMessage}
215227
/>
216228
</div>
217229
</div>

web/src/hooks/useSessionManager.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ vi.mock('@/api/client', () => ({
88
get: vi.fn(),
99
post: vi.fn(),
1010
put: vi.fn(),
11+
patch: vi.fn(),
1112
delete: vi.fn(),
1213
},
1314
}));
@@ -18,6 +19,7 @@ const mockApiClient = apiClient as {
1819
get: ReturnType<typeof vi.fn>;
1920
post: ReturnType<typeof vi.fn>;
2021
put: ReturnType<typeof vi.fn>;
22+
patch: ReturnType<typeof vi.fn>;
2123
delete: ReturnType<typeof vi.fn>;
2224
};
2325

0 commit comments

Comments
 (0)