-
Notifications
You must be signed in to change notification settings - Fork 360
fix: make agent_names hot-reloadable via ArcSwap to fix unknown agent errors #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -448,12 +448,8 @@ impl LoopGuard { | |||||||||||||||||||||||||
| hasher.update(b"|"); | ||||||||||||||||||||||||||
| hasher.update(args.as_bytes()); | ||||||||||||||||||||||||||
| hasher.update(b"|"); | ||||||||||||||||||||||||||
| let truncated = if result.len() > RESULT_HASH_TRUNCATION { | ||||||||||||||||||||||||||
| &result[..RESULT_HASH_TRUNCATION] | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| result | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| hasher.update(truncated.as_bytes()); | ||||||||||||||||||||||||||
| let result_bytes = result.as_bytes(); | ||||||||||||||||||||||||||
| hasher.update(&result_bytes[..result_bytes.len().min(RESULT_HASH_TRUNCATION)]); | ||||||||||||||||||||||||||
|
Comment on lines
+451
to
+452
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefix-only hashing still treats appended output as identical. This still hashes only the first 🔧 Possible fix- let result_bytes = result.as_bytes();
- hasher.update(&result_bytes[..result_bytes.len().min(RESULT_HASH_TRUNCATION)]);
+ let result_bytes = result.as_bytes();
+ if result_bytes.len() <= RESULT_HASH_TRUNCATION {
+ hasher.update(result_bytes);
+ } else {
+ let prefix_len = RESULT_HASH_TRUNCATION / 2;
+ let suffix_len = RESULT_HASH_TRUNCATION - prefix_len;
+ hasher.update(&result_bytes[..prefix_len]);
+ hasher.update(&(result_bytes.len() as u64).to_le_bytes());
+ hasher.update(&result_bytes[result_bytes.len() - suffix_len..]);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| hex::encode(hasher.finalize()) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -393,7 +393,8 @@ pub struct AgentDeps { | |||||||||||
| pub sandbox: Arc<sandbox::Sandbox>, | ||||||||||||
| pub links: Arc<arc_swap::ArcSwap<Vec<links::AgentLink>>>, | ||||||||||||
| /// Map of all agent IDs to display names, for inter-agent message routing. | ||||||||||||
| pub agent_names: Arc<std::collections::HashMap<String, String>>, | ||||||||||||
| /// Hot-reloadable: updated when agents are added or reconfigured. | ||||||||||||
| pub agent_names: Arc<arc_swap::ArcSwap<std::collections::HashMap<String, String>>>, | ||||||||||||
|
Comment on lines
+396
to
+397
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Expect at least one stale initializer if any fixtures still use the old type.
rg -n -U -P 'agent_names:\s*Arc::new\(\s*(?:std::collections::)?HashMap::new\(\)\s*\)' --type rustRepository: spacedriveapp/spacebot Length of output: 243 Update remaining Two test fixtures still initialize
Both need an Expected fix- agent_names: Arc::new(std::collections::HashMap::new()),
+ agent_names: Arc::new(arc_swap::ArcSwap::from_pointee(
+ std::collections::HashMap::new(),
+ )),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| /// Org-level human definitions (hot-reloadable). Used by `build_org_context()` | ||||||||||||
| /// to surface human display names, roles, and descriptions in agent prompts. | ||||||||||||
| pub humans: Arc<arc_swap::ArcSwap<Vec<config::HumanDef>>>, | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1444,13 +1444,18 @@ async fn run( | |
| ArcSwap<std::collections::HashMap<String, Arc<spacebot::tasks::TaskStore>>>, | ||
| > = Arc::new(ArcSwap::from_pointee(std::collections::HashMap::new())); | ||
|
|
||
| let agent_names_registry: Arc< | ||
| ArcSwap<std::collections::HashMap<String, String>>, | ||
| > = Arc::new(ArcSwap::from_pointee(std::collections::HashMap::new())); | ||
|
|
||
| // Start HTTP API server if enabled | ||
| let mut api_state = spacebot::api::ApiState::new_with_provider_sender( | ||
| provider_tx, | ||
| agent_tx, | ||
| agent_remove_tx, | ||
| injection_tx.clone(), | ||
| task_store_registry.clone(), | ||
| agent_names_registry.clone(), | ||
|
Comment on lines
1452
to
+1458
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the remaining
Suggested fix in
|
||
| ); | ||
| api_state.auth_token = config.api.auth_token.clone(); | ||
| let api_state = Arc::new(api_state); | ||
|
|
@@ -1608,6 +1613,7 @@ async fn run( | |
| agent_humans.clone(), | ||
| injection_tx.clone(), | ||
| task_store_registry.clone(), | ||
| agent_names_registry.clone(), | ||
| &bootstrapped_store, | ||
| ) | ||
| .await?; | ||
|
|
@@ -2337,6 +2343,7 @@ async fn run( | |
| agent_humans.clone(), | ||
| injection_tx.clone(), | ||
| task_store_registry.clone(), | ||
| agent_names_registry.clone(), | ||
| &bootstrapped_store, | ||
| ).await { | ||
| Ok(()) => { | ||
|
|
@@ -2478,20 +2485,21 @@ async fn initialize_agents( | |
| task_store_registry: Arc< | ||
| ArcSwap<std::collections::HashMap<String, Arc<spacebot::tasks::TaskStore>>>, | ||
| >, | ||
| agent_names_registry: Arc<ArcSwap<std::collections::HashMap<String, String>>>, | ||
| bootstrapped_store: &Option<Arc<spacebot::secrets::store::SecretsStore>>, | ||
| ) -> anyhow::Result<()> { | ||
| let resolved_agents = config.resolve_agents(); | ||
|
|
||
| // Build agent name map for inter-agent message routing | ||
| let agent_name_map: Arc<std::collections::HashMap<String, String>> = Arc::new( | ||
| resolved_agents | ||
| .iter() | ||
| .map(|a| { | ||
| let name = a.display_name.clone().unwrap_or_else(|| a.id.clone()); | ||
| (a.id.clone(), name) | ||
| }) | ||
| .collect(), | ||
| ); | ||
| // Build agent name map and publish to the shared registry so all agents | ||
| // (including those already running) see the updated names immediately. | ||
| let agent_name_map: std::collections::HashMap<String, String> = resolved_agents | ||
| .iter() | ||
| .map(|a| { | ||
| let name = a.display_name.clone().unwrap_or_else(|| a.id.clone()); | ||
| (a.id.clone(), name) | ||
| }) | ||
| .collect(); | ||
| agent_names_registry.store(Arc::new(agent_name_map)); | ||
|
|
||
| for agent_config in &resolved_agents { | ||
| tracing::info!(agent_id = %agent_config.id, "initializing agent"); | ||
|
|
@@ -2713,7 +2721,7 @@ async fn initialize_agents( | |
| messaging_manager: None, | ||
| sandbox, | ||
| links: agent_links.clone(), | ||
| agent_names: agent_name_map.clone(), | ||
| agent_names: agent_names_registry.clone(), | ||
| humans: agent_humans.clone(), | ||
| task_store_registry: task_store_registry.clone(), | ||
| process_control_registry: Arc::new( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This clone+store can drop updates if multiple agent creations happen concurrently (last writer wins).
ArcSwap::rcugives you an atomic read-modify-write loop.