mirror of
https://github.com/davidkaya/aryx.git
synced 2026-07-24 21:48:36 +02:00
fix: align chat MCP tooling with session project
Resolve chat tooling from the selected session's project instead of the separately tracked selectedProjectId so project-scoped discovered MCPs do not appear for the wrong session. Also synchronize selectedProjectId when a session is selected and add regression tests for both behaviors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -852,6 +852,18 @@ export class EryxAppService extends EventEmitter<AppServiceEvents> {
|
||||
|
||||
async selectSession(sessionId?: string): Promise<WorkspaceState> {
|
||||
const workspace = await this.loadWorkspace();
|
||||
if (sessionId) {
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
const project = this.requireProject(workspace, session.projectId);
|
||||
const didSyncProjectTooling = await this.syncProjectDiscoveredTooling(workspace, project);
|
||||
if (didSyncProjectTooling) {
|
||||
this.pruneUnavailableSessionToolingSelections(workspace);
|
||||
await this.pruneUnavailableApprovalTools(workspace);
|
||||
}
|
||||
|
||||
workspace.selectedProjectId = session.projectId;
|
||||
}
|
||||
|
||||
workspace.selectedSessionId = sessionId;
|
||||
return this.persistAndBroadcast(workspace);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { DiscoveredToolingModal } from '@renderer/components/DiscoveredToolingMo
|
||||
import { NewSessionModal } from '@renderer/components/NewSessionModal';
|
||||
import { SettingsPanel } from '@renderer/components/SettingsPanel';
|
||||
import { Sidebar } from '@renderer/components/Sidebar';
|
||||
import { resolveChatToolingSettings } from '@renderer/lib/chatTooling';
|
||||
import {
|
||||
applySessionEventActivity,
|
||||
pruneSessionActivities,
|
||||
@@ -28,7 +29,6 @@ import { syncPatternGraph, type PatternDefinition } from '@shared/domain/pattern
|
||||
import { isScratchpadProject, SCRATCHPAD_PROJECT_ID } from '@shared/domain/project';
|
||||
import { applyScratchpadSessionConfig } from '@shared/domain/session';
|
||||
import type { AppearanceTheme, LspProfileDefinition, McpServerDefinition } from '@shared/domain/tooling';
|
||||
import { resolveProjectToolingSettings } from '@shared/domain/tooling';
|
||||
import type { WorkspaceState } from '@shared/domain/workspace';
|
||||
import { createId, nowIso } from '@shared/utils/ids';
|
||||
|
||||
@@ -180,9 +180,9 @@ export default function App() {
|
||||
[workspace?.projects, workspace?.selectedProjectId],
|
||||
);
|
||||
|
||||
const effectiveTooling = useMemo(
|
||||
() => workspace ? resolveProjectToolingSettings(workspace.settings, selectedProject?.discoveredTooling) : undefined,
|
||||
[workspace?.settings, selectedProject?.discoveredTooling],
|
||||
const chatToolingSettings = useMemo(
|
||||
() => resolveChatToolingSettings(workspace, projectForSession),
|
||||
[projectForSession, workspace],
|
||||
);
|
||||
|
||||
const hasPendingDiscoveries = useMemo(() => {
|
||||
@@ -274,7 +274,7 @@ export default function App() {
|
||||
project={projectForSession}
|
||||
runtimeTools={sidecarCapabilities?.runtimeTools}
|
||||
session={selectedSession}
|
||||
toolingSettings={effectiveTooling ?? workspace.settings.tooling}
|
||||
toolingSettings={chatToolingSettings ?? workspace.settings.tooling}
|
||||
/>
|
||||
);
|
||||
detailPanel = (
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { ProjectRecord } from '@shared/domain/project';
|
||||
import { resolveProjectToolingSettings, type WorkspaceToolingSettings } from '@shared/domain/tooling';
|
||||
import type { WorkspaceState } from '@shared/domain/workspace';
|
||||
|
||||
export function resolveChatToolingSettings(
|
||||
workspace: WorkspaceState | undefined,
|
||||
projectForSession: ProjectRecord | undefined,
|
||||
): WorkspaceToolingSettings | undefined {
|
||||
if (!workspace) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveProjectToolingSettings(workspace.settings, projectForSession?.discoveredTooling);
|
||||
}
|
||||
@@ -224,4 +224,37 @@ describe('EryxAppService discovered tooling', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('selecting a session also selects that session project', async () => {
|
||||
const workspace = createWorkspaceSeed();
|
||||
const pattern = workspace.patterns.find((candidate) => candidate.mode === 'single');
|
||||
if (!pattern) {
|
||||
throw new Error('Expected a single-agent pattern in the workspace seed.');
|
||||
}
|
||||
|
||||
const projectAlpha = createProject({
|
||||
id: 'project-alpha',
|
||||
name: 'alpha',
|
||||
path: 'C:\\workspace\\alpha',
|
||||
});
|
||||
const projectBeta = createProject({
|
||||
id: 'project-beta',
|
||||
name: 'beta',
|
||||
path: 'C:\\workspace\\beta',
|
||||
});
|
||||
const session = createSession(projectBeta.id, pattern.id, {
|
||||
id: 'session-beta',
|
||||
});
|
||||
|
||||
workspace.projects = [projectAlpha, projectBeta];
|
||||
workspace.sessions = [session];
|
||||
workspace.selectedProjectId = projectAlpha.id;
|
||||
|
||||
const service = createService(workspace, pattern);
|
||||
|
||||
const updatedWorkspace = await service.selectSession(session.id);
|
||||
|
||||
expect(updatedWorkspace.selectedSessionId).toBe(session.id);
|
||||
expect(updatedWorkspace.selectedProjectId).toBe(projectBeta.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
|
||||
import { resolveChatToolingSettings } from '@renderer/lib/chatTooling';
|
||||
import type { DiscoveredLocalMcpServer } from '@shared/domain/discoveredTooling';
|
||||
import { listPendingDiscoveredMcpServers, normalizeDiscoveredToolingState, type DiscoveredToolingState } from '@shared/domain/discoveredTooling';
|
||||
import type { ProjectRecord } from '@shared/domain/project';
|
||||
import { resolveProjectToolingSettings, createWorkspaceSettings, type WorkspaceSettings } from '@shared/domain/tooling';
|
||||
import { createWorkspaceSeed } from '@shared/domain/workspace';
|
||||
|
||||
function makeDiscoveredServer(
|
||||
overrides: Partial<DiscoveredLocalMcpServer> & { id: string; name: string },
|
||||
@@ -117,4 +120,49 @@ describe('frontend discovered tooling integration', () => {
|
||||
expect(projectDiscovered).toHaveLength(1);
|
||||
expect(projectDiscovered[0].name).toBe('proj-srv');
|
||||
});
|
||||
|
||||
test('chat tooling follows the session project instead of the separately selected project', () => {
|
||||
const workspace = createWorkspaceSeed();
|
||||
const sessionProject: ProjectRecord = {
|
||||
id: 'project-session',
|
||||
name: 'session-project',
|
||||
path: 'C:\\workspace\\session-project',
|
||||
addedAt: '2026-03-25T00:00:00.000Z',
|
||||
discoveredTooling: normalizeDiscoveredToolingState({
|
||||
mcpServers: [
|
||||
makeDiscoveredServer({
|
||||
id: 'discovered_project_project_session_vscode_mcp_session_srv',
|
||||
name: 'session-srv',
|
||||
status: 'accepted',
|
||||
scope: 'project',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
const separatelySelectedProject: ProjectRecord = {
|
||||
id: 'project-selected',
|
||||
name: 'selected-project',
|
||||
path: 'C:\\workspace\\selected-project',
|
||||
addedAt: '2026-03-25T00:00:00.000Z',
|
||||
discoveredTooling: normalizeDiscoveredToolingState({
|
||||
mcpServers: [
|
||||
makeDiscoveredServer({
|
||||
id: 'discovered_project_project_selected_vscode_mcp_selected_srv',
|
||||
name: 'selected-srv',
|
||||
status: 'accepted',
|
||||
scope: 'project',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
|
||||
workspace.projects = [sessionProject, separatelySelectedProject];
|
||||
workspace.selectedProjectId = separatelySelectedProject.id;
|
||||
|
||||
const tooling = resolveChatToolingSettings(workspace, sessionProject);
|
||||
const serverIds = tooling?.mcpServers.map((server) => server.id) ?? [];
|
||||
|
||||
expect(serverIds).toContain('discovered_project_project_session_vscode_mcp_session_srv');
|
||||
expect(serverIds).not.toContain('discovered_project_project_selected_vscode_mcp_selected_srv');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user