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

@@ -45,7 +45,7 @@ pub enum IdentityFile {
Instructions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ProposalStatus {
Pending,
@@ -230,21 +230,24 @@ impl AgentIdentityManager {
.position(|p| p.id == proposal_id && p.status == ProposalStatus::Pending)
.ok_or_else(|| "Proposal not found or not pending".to_string())?;
let proposal = &self.proposals[proposal_idx];
// Clone all needed data before mutating
let proposal = self.proposals[proposal_idx].clone();
let agent_id = proposal.agent_id.clone();
let file = proposal.file.clone();
let reason = proposal.reason.clone();
let suggested_content = proposal.suggested_content.clone();
// Create snapshot before applying
self.create_snapshot(&agent_id, &format!("Approved proposal: {}", proposal.reason));
self.create_snapshot(&agent_id, &format!("Approved proposal: {}", reason));
// Get current identity and update
let identity = self.get_identity(&agent_id);
let mut updated = identity.clone();
match file {
IdentityFile::Soul => updated.soul = proposal.suggested_content.clone(),
IdentityFile::Soul => updated.soul = suggested_content,
IdentityFile::Instructions => {
updated.instructions = proposal.suggested_content.clone()
updated.instructions = suggested_content
}
}
@@ -324,16 +327,18 @@ impl AgentIdentityManager {
.snapshots
.iter()
.filter(|s| s.agent_id == agent_id)
.cloned()
.collect();
if agent_snapshots.len() > 50 {
// Remove oldest snapshots for this agent
// Keep only the 50 most recent snapshots for this agent
let ids_to_keep: std::collections::HashSet<_> = agent_snapshots
.iter()
.rev()
.take(50)
.map(|s| s.id.clone())
.collect();
self.snapshots.retain(|s| {
s.agent_id != agent_id
|| agent_snapshots
.iter()
.rev()
.take(50)
.any(|&s_ref| s_ref.id == s.id)
s.agent_id != agent_id || ids_to_keep.contains(&s.id)
});
}
}
@@ -355,16 +360,21 @@ impl AgentIdentityManager {
.snapshots
.iter()
.find(|s| s.agent_id == agent_id && s.id == snapshot_id)
.ok_or_else(|| "Snapshot not found".to_string())?;
.ok_or_else(|| "Snapshot not found".to_string())?
.clone();
// Clone files before creating new snapshot
let files = snapshot.files.clone();
let timestamp = snapshot.timestamp.clone();
// Create snapshot before rollback
self.create_snapshot(
agent_id,
&format!("Rollback to {}", snapshot.timestamp),
&format!("Rollback to {}", timestamp),
);
self.identities
.insert(agent_id.to_string(), snapshot.files.clone());
.insert(agent_id.to_string(), files);
Ok(())
}