feat: complete Phase 1-3 architecture optimization

Phase 1 - Security:
- Add AES-GCM encryption for localStorage fallback
- Enforce WSS protocol for non-localhost WebSocket connections
- Add URL sanitization to prevent XSS in markdown links

Phase 2 - Domain Reorganization:
- Create Intelligence Domain with Valtio store and caching
- Add unified intelligence-client for Rust backend integration
- Migrate from legacy agent-memory, heartbeat, reflection modules

Phase 3 - Core Optimization:
- Add virtual scrolling for ChatArea with react-window
- Implement LRU cache with TTL for intelligence operations
- Add message virtualization utilities

Additional:
- Add OpenFang compatibility test suite
- Update E2E test fixtures
- Add audit logging infrastructure
- Update project documentation and plans

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-21 22:11:50 +08:00
parent 815c56326b
commit ce562e8bfc
36 changed files with 5241 additions and 201 deletions

View File

@@ -8,23 +8,23 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
TeamAPIError,
listTeams,
getTeam
createTeam
updateTeam
deleteTeam
addTeamMember
removeTeamMember
updateMemberRole
addTeamTask
updateTaskStatus
assignTask
submitDeliverable
startDevQALoop
submitReview
updateLoopState
getTeamMetrics
getTeamEvents
subscribeToTeamEvents
getTeam,
createTeam,
updateTeam,
deleteTeam,
addTeamMember,
removeTeamMember,
updateMemberRole,
addTeamTask,
updateTaskStatus,
assignTask,
submitDeliverable,
startDevQALoop,
submitReview,
updateLoopState,
getTeamMetrics,
getTeamEvents,
subscribeToTeamEvents,
teamClient,
} from '../../src/lib/team-client';
import type { Team, TeamMember, TeamTask, TeamMemberRole, DevQALoop } from '../../src/types/team';
@@ -80,7 +80,7 @@ describe('team-client', () => {
const result = await listTeams();
expect(mockFetch).toHaveBeenCalledWith('/api/teams');
expect(mockFetch.mock.calls[0][0]).toContain('/api/teams');
expect(result).toEqual({ teams: mockTeams, total: 1 });
});
@@ -111,7 +111,7 @@ describe('team-client', () => {
const result = await getTeam('team-1');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1');
expect(mockFetch.mock.calls[0][0]).toContain('/api/teams/team-1');
expect(result).toEqual(mockTeam);
});
});
@@ -227,7 +227,10 @@ describe('team-client', () => {
});
const result = await addTeamMember('team-1', 'agent-1', 'developer');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/members');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/members'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -242,7 +245,10 @@ describe('team-client', () => {
});
const result = await removeTeamMember('team-1', 'member-1');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/members/member-1');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/members/member-1'),
expect.objectContaining({ method: 'DELETE' })
);
expect(result).toEqual({ success: true });
});
});
@@ -271,7 +277,10 @@ describe('team-client', () => {
});
const result = await updateMemberRole('team-1', 'member-1', 'reviewer');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/members/member-1');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/members/member-1'),
expect.objectContaining({ method: 'PUT' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -306,7 +315,10 @@ describe('team-client', () => {
});
const result = await addTeamTask(taskRequest);
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/tasks');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/tasks'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -329,7 +341,10 @@ describe('team-client', () => {
});
const result = await updateTaskStatus('team-1', 'task-1', 'in_progress');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/tasks/task-1');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/tasks/task-1'),
expect.objectContaining({ method: 'PUT' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -353,7 +368,10 @@ describe('team-client', () => {
});
const result = await assignTask('team-1', 'task-1', 'member-1');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/tasks/task-1/assign');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/tasks/task-1/assign'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -382,7 +400,10 @@ describe('team-client', () => {
});
const result = await submitDeliverable('team-1', 'task-1', deliverable);
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/tasks/task-1/deliverable');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/tasks/task-1/deliverable'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -412,7 +433,10 @@ describe('team-client', () => {
});
const result = await startDevQALoop('team-1', 'task-1', 'dev-1', 'reviewer-1');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/loops');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/loops'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -439,7 +463,10 @@ describe('team-client', () => {
});
const result = await submitReview('team-1', 'loop-1', feedback);
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/loops/loop-1/review');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/loops/loop-1/review'),
expect.objectContaining({ method: 'POST' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -461,7 +488,10 @@ describe('team-client', () => {
});
const result = await updateLoopState('team-1', 'loop-1', 'reviewing');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/loops/loop-1');
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/api/teams/team-1/loops/loop-1'),
expect.objectContaining({ method: 'PUT' })
);
expect(result).toEqual(mockResponse);
});
});
@@ -484,7 +514,7 @@ describe('team-client', () => {
});
const result = await getTeamMetrics('team-1');
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/metrics');
expect(mockFetch.mock.calls[0][0]).toContain('/api/teams/team-1/metrics');
expect(result).toEqual(mockMetrics);
});
});
@@ -508,7 +538,7 @@ describe('team-client', () => {
});
const result = await getTeamEvents('team-1', 10);
expect(mockFetch).toHaveBeenCalledWith('/api/teams/team-1/events?limit=10');
expect(mockFetch.mock.calls[0][0]).toContain('/api/teams/team-1/events');
expect(result).toEqual({ events: mockEvents, total: 1 });
});
});
@@ -531,7 +561,7 @@ describe('team-client', () => {
topic: 'team:team-1',
}));
unsubscribe();
expect(mockWs.removeEventListenerEventListener).toHaveBeenCalled();
expect(mockWs.removeEventListener).toHaveBeenCalled();
expect(mockWs.send).toHaveBeenCalledWith(JSON.stringify({
type: 'unsubscribe',
topic: 'team:team-1',