Skip to main content

awaken_runtime/extensions/handoff/
plugin.rs

1use std::collections::HashMap;
2
3use awaken_contract::StateError;
4use awaken_contract::contract::active_agent::ActiveAgentIdKey;
5use awaken_contract::model::Phase;
6use awaken_contract::registry_spec::AgentSpec;
7
8use crate::plugins::{Plugin, PluginDescriptor, PluginRegistrar};
9use crate::state::{KeyScope, MutationBatch, StateKeyOptions};
10
11use super::action::HandoffAction;
12use super::hook::HandoffSyncHook;
13use super::state::{ActiveAgentKey, HandoffState};
14use super::types::AgentOverlay;
15
16/// Stable plugin ID for handoff.
17pub const HANDOFF_PLUGIN_ID: &str = "agent_handoff";
18
19/// Dynamic agent handoff plugin.
20///
21/// Applies agent overlays dynamically within the running agent loop.
22/// Configured with a map of agent variant name -> overlay.
23pub struct HandoffPlugin {
24    overlays: HashMap<String, AgentOverlay>,
25}
26
27impl HandoffPlugin {
28    /// Create a new handoff plugin with the given agent variant overlays.
29    pub fn new(overlays: HashMap<String, AgentOverlay>) -> Self {
30        Self { overlays }
31    }
32
33    /// Get the overlay for a given agent variant.
34    pub fn overlay(&self, agent: &str) -> Option<&AgentOverlay> {
35        self.overlays.get(agent)
36    }
37
38    /// Get the effective agent ID from the handoff state.
39    pub fn effective_agent(state: &HandoffState) -> Option<&String> {
40        state
41            .requested_agent
42            .as_ref()
43            .or(state.active_agent.as_ref())
44    }
45}
46
47impl Plugin for HandoffPlugin {
48    fn descriptor(&self) -> PluginDescriptor {
49        PluginDescriptor {
50            name: HANDOFF_PLUGIN_ID,
51        }
52    }
53
54    fn register(&self, registrar: &mut PluginRegistrar) -> Result<(), StateError> {
55        let thread_scope = StateKeyOptions {
56            scope: KeyScope::Thread,
57            persistent: true,
58            ..StateKeyOptions::default()
59        };
60        registrar.register_key::<ActiveAgentKey>(thread_scope)?;
61        registrar.register_key::<ActiveAgentIdKey>(thread_scope)?;
62        registrar.register_phase_hook(HANDOFF_PLUGIN_ID, Phase::RunStart, HandoffSyncHook)?;
63        registrar.register_phase_hook(HANDOFF_PLUGIN_ID, Phase::StepEnd, HandoffSyncHook)?;
64        Ok(())
65    }
66
67    fn on_activate(
68        &self,
69        _agent_spec: &AgentSpec,
70        _patch: &mut MutationBatch,
71    ) -> Result<(), StateError> {
72        Ok(())
73    }
74
75    fn on_deactivate(&self, patch: &mut MutationBatch) -> Result<(), StateError> {
76        patch.update::<ActiveAgentKey>(HandoffAction::Clear);
77        patch.update::<ActiveAgentIdKey>(None);
78        Ok(())
79    }
80}