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:
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user