fix: synchronize task collaboration session titles

Apply creation titles and repaired history names to cached sessions.
Keep messages and reading state intact and preserve title edits received
while a collaboration request is in flight.

Validation: pnpm lint; pnpm typecheck; 604 unit tests; two browser regressions.
This commit is contained in:
2026-10-29 16:55:31 +08:00
parent 5b710d1247
commit 7621bf0f86
4 changed files with 79 additions and 1 deletions
@@ -62,6 +62,7 @@ import { agentRuntimes, createAgentRuntime, mergeHistoryMessages, resumeAgentRun
type AgentReadingPosition, type AgentSessionRuntime } from "./agent-session-runtime";
const AGENT_PANEL_COLLAPSE_MS = 180;
const LEGACY_COLLABORATION_TITLES = new Set(["任务协作", "任务协作问答", "新对话"]);
type UseWorkbenchAgentOptions = {
onUiEnvelope: (payload: UIEnvelopePayload, sessionId: string) => Promise<void> | void;
@@ -334,6 +335,20 @@ export function useWorkbenchAgent({
// Ownership changes only when selecting a different runtime, never on token updates.
if (resources.activeAgent !== runtime) activateRuntime(runtime);
}, [activateRuntime, resources, runtime]);
useEffect(() => {
let changed = false;
const summaries = new Map(history.sessions.map((summary) => [summary.id, summary]));
for (const entry of agentRuntimes(resources)) {
if (entry.scope !== scopeKey || entry.purpose !== "task_collaboration"
|| !LEGACY_COLLABORATION_TITLES.has(entry.title)) continue;
const summary = entry.sessionId ? summaries.get(entry.sessionId) : undefined;
if (!summary || LEGACY_COLLABORATION_TITLES.has(summary.title)) continue;
entry.title = summary.title;
if (resources.activeAgent === entry) setSessionTitle(summary.title);
changed = true;
}
if (changed) resources.notify();
}, [history.sessions, resources, scopeKey]);
useEffect(() => {
chatRef.current = chat;
}, [chat]);
@@ -642,6 +657,7 @@ export function useWorkbenchAgent({
if (!normalizedTaskId) throw new Error("工况任务 ID 不能为空。");
const context = agentTaskDiscussionContextSchema.parse(discussion?.context ?? {});
const sourceDraft = resources.activeAgent?.draft ?? promptDraftRef.current;
const titlesBeforeOpen = new Map(agentRuntimes(resources).filter((entry) => entry.scope === projectScopeKey).map((entry) => [entry, entry.title]));
const projectClient = createAgentApiClient(undefined, { getAccessToken: getWorkbenchTokenProvider(getAccessToken), getProjectId });
const collaboration = await projectClient.createTaskCollaborationSession(normalizedTaskId);
const id = collaboration.session_id || collaboration.id;
@@ -654,6 +670,10 @@ export function useWorkbenchAgent({
createWorkbenchAgentTransport(projectClient, getAccessToken, getProjectId), loaded);
if (loaded.isStreaming || loaded.runStatus === "running") resumeAgentRuntime(resources, target);
}
const titleBeforeOpen = titlesBeforeOpen.get(target);
const canApplyTitle = titleBeforeOpen === undefined
? LEGACY_COLLABORATION_TITLES.has(target.title) : target.title === titleBeforeOpen;
if (collaboration.title?.trim() && canApplyTitle) target.title = collaboration.title.trim();
target.taskDiscussion = { context, reportGeneration: discussion?.reportGeneration };
if (!target.draft.trim() && target !== resources.activeAgent) target.draft = sourceDraft;
if (discussion?.prompt?.trim()) target.draft = target.draft.trim() ? `${target.draft}\n\n${discussion.prompt.trim()}` : discussion.prompt.trim();