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

@@ -8,6 +8,7 @@ import {
type CodingConversationV2,
} from './conversation-store';
import {
ensureDefaultCodingAgent,
isCanonicalCodingProjectId,
normalizeCodingProjectConfigV2,
normalizeProjectIdentityChoice,
@@ -60,6 +61,7 @@ export interface CodingProjectConfigSnapshot {
}
export interface CodingProjectServiceOptions {
getDefaultModel?(): Promise<import('../../shared/coding-conversation-contracts').ProductModelRef | null>;
createProjectId?: () => string;
now?: () => string;
onResourcesChanged?(project: CodingProject): Promise<void> | void;
@@ -287,6 +289,7 @@ export class CodingProjectService {
const { project, config } = await createLocalCodingProject({
projectPath,
projectId,
defaultModel: await this.options.getDefaultModel?.().catch(() => null),
...(input.projectType ? { projectType: input.projectType } : {}),
}, this.store);
const snapshot = { project, config, knowledgeFiles: [] };
@@ -338,7 +341,8 @@ export class CodingProjectService {
async getConfig(projectId?: string): Promise<CodingProjectConfigSnapshot> {
const project = projectId ? await this.getProject(projectId) : await this.requireActiveProject();
const config = await this.requireCurrentConfig(project);
return config.projectId
return config.projectId && config.initialized && config.agents.some((agent) =>
agent.id === config.defaultAgentId && agent.enabled && !agent.archivedAt)
? await this.projectConfigSnapshot(project, config)
: await this.ensureProjectIdentity(project);
}
@@ -530,20 +534,21 @@ export class CodingProjectService {
): Promise<CodingProjectConfigSnapshot> {
return await this.serializeIdentityTransition(project.path, async () => {
const current = await this.requireCurrentConfig(project);
if (current.projectId) {
const normalized = ensureDefaultCodingAgent(current, this.options.now?.());
if (current.projectId && normalized === current) {
return await this.projectConfigSnapshot(project, current);
}
const nextProjectId = resolveProjectIdentity(
const nextProjectId = current.projectId ?? resolveProjectIdentity(
{ kind: 'create' },
this.options.createProjectId ?? randomUUID,
);
await this.options.onProjectIdentityChanging?.(
if (!current.projectId) await this.options.onProjectIdentityChanging?.(
project,
undefined,
nextProjectId,
);
const next = {
...current,
...normalized,
projectId: nextProjectId,
updatedAt: this.options.now?.() ?? new Date().toISOString(),
};