feat: organize project conversations and add coding teacher side chat

This commit is contained in:
2026-09-22 12:30:26 +08:00
parent 2f82b9f79c
commit 9242a40d87
45 changed files with 2987 additions and 1137 deletions

View File

@@ -30,7 +30,7 @@ interface CodingWorkspaceDependencies {
listConversations(projectId: string): Promise<CodingConversationMetadata[]>;
createConversation(input: {
projectId: string;
agentId: string;
agentId?: string;
title: string;
}): Promise<CodingConversationMetadata>;
patchConversation(
@@ -74,7 +74,7 @@ export interface CodingWorkspaceState {
removeProject(projectId: string): Promise<void>;
selectAgent(agentId: string): void;
ensureConversation(agentId: string): Promise<CodingConversationMetadata>;
createConversation(agentId: string): Promise<CodingConversationMetadata>;
createConversation(agentId?: string): Promise<CodingConversationMetadata>;
patchConversation(
conversationId: string,
patch: { title?: string; archived?: boolean; unread?: boolean },
@@ -90,7 +90,8 @@ function enabledAgent(config: CodingProjectConfig | null, agentId: string | null
}
function firstEnabledAgent(config: CodingProjectConfig): CodingProjectAgent | null {
return config.agents.find((agent) => agent.enabled && !agent.archivedAt && agent.pinned)
return config.agents.find((agent) => agent.id === config.defaultAgentId && agent.enabled && !agent.archivedAt)
?? config.agents.find((agent) => agent.enabled && !agent.archivedAt && agent.pinned)
?? config.agents.find((agent) => agent.enabled && !agent.archivedAt)
?? null;
}
@@ -142,6 +143,8 @@ export function createCodingWorkspaceStore(
let loadGeneration = 0;
let metadataRevision = 0;
let metadataTail = Promise.resolve();
let activationIntent = 0;
let activationTail: Promise<unknown> = Promise.resolve();
const refreshFlights = new Map<string, { dirty: boolean; promise: Promise<void> }>();
return createStore<CodingWorkspaceState>((set, get) => ({
@@ -252,8 +255,13 @@ export function createCodingWorkspaceStore(
},
async setActiveProject(projectId) {
const project = await deps.setActiveProject(projectId);
await get().load();
const intent = ++activationIntent;
++loadGeneration;
loadFlight = null;
const activation = activationTail.catch(() => undefined).then(() => deps.setActiveProject(projectId));
activationTail = activation;
const project = await activation;
if (intent === activationIntent) await get().load();
return project;
},
@@ -275,14 +283,16 @@ export function createCodingWorkspaceStore(
async createConversation(agentId) {
const state = get();
if (!state.activeProject || !enabledAgent(state.config, agentId)) {
agentId ??= state.config?.defaultAgentId ?? (state.config ? firstEnabledAgent(state.config)?.id : undefined);
if (!state.activeProject || !enabledAgent(state.config, agentId ?? null)) {
throw new Error('当前项目没有可用的智能体。');
}
const flightKey = `${state.activeProject.id}:${agentId}`;
const resolvedAgentId = agentId!;
const flightKey = `${state.activeProject.id}:${resolvedAgentId}`;
const existingFlight = conversationFlights.get(flightKey);
if (existingFlight) return await existingFlight;
set((current) => ({
creatingAgentIds: { ...current.creatingAgentIds, [agentId]: true },
creatingAgentIds: { ...current.creatingAgentIds, [resolvedAgentId]: true },
error: null,
}));
let flight: Promise<CodingConversationMetadata>;
@@ -307,7 +317,7 @@ export function createCodingWorkspaceStore(
conversationFlights.delete(flightKey);
set((current) => {
const creatingAgentIds = { ...current.creatingAgentIds };
delete creatingAgentIds[agentId];
delete creatingAgentIds[resolvedAgentId];
return { creatingAgentIds };
});
});