#[derive(Debug, Default)]
pub(in crate::agent) struct PendingDirectives {
pending_background_ack: Option<String>,
pending_external_action_ack: Option<String>,
pending_system_messages: Vec<crate::agent::SystemDirective>,
identity_prefill_text: Option<String>,
cli_agent_boundary_injected: bool,
}
pub(in crate::agent) struct StoppingDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
pub pending_background_ack: &'a mut Option<String>,
}
pub(in crate::agent) struct MessageBuildDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
}
pub(in crate::agent) struct LlmDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
pub pending_external_action_ack: &'a mut Option<String>,
pub identity_prefill_text: &'a mut Option<String>,
}
pub(in crate::agent) struct ResponseDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
pub identity_prefill_text: &'a mut Option<String>,
pub pending_background_ack: &'a mut Option<String>,
pub pending_external_action_ack: &'a mut Option<String>,
}
pub(in crate::agent) struct ToolPreludeDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
}
pub(in crate::agent) struct ToolExecutionDirectivesState<'a> {
pub pending_system_messages: &'a mut Vec<crate::agent::SystemDirective>,
pub cli_agent_boundary_injected: &'a mut bool,
pub pending_background_ack: &'a mut Option<String>,
pub pending_external_action_ack: &'a mut Option<String>,
}
impl PendingDirectives {
pub(in crate::agent) fn for_stopping_phase(&mut self) -> StoppingDirectivesState<'_> {
StoppingDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
pending_background_ack: &mut self.pending_background_ack,
}
}
pub(in crate::agent) fn for_message_build_phase(&mut self) -> MessageBuildDirectivesState<'_> {
MessageBuildDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
}
}
pub(in crate::agent) fn for_llm_phase(&mut self) -> LlmDirectivesState<'_> {
LlmDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
pending_external_action_ack: &mut self.pending_external_action_ack,
identity_prefill_text: &mut self.identity_prefill_text,
}
}
pub(in crate::agent) fn for_response_phase(&mut self) -> ResponseDirectivesState<'_> {
ResponseDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
identity_prefill_text: &mut self.identity_prefill_text,
pending_background_ack: &mut self.pending_background_ack,
pending_external_action_ack: &mut self.pending_external_action_ack,
}
}
pub(in crate::agent) fn for_tool_prelude_phase(&mut self) -> ToolPreludeDirectivesState<'_> {
ToolPreludeDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
}
}
pub(in crate::agent) fn for_tool_execution_phase(
&mut self,
) -> ToolExecutionDirectivesState<'_> {
ToolExecutionDirectivesState {
pending_system_messages: &mut self.pending_system_messages,
cli_agent_boundary_injected: &mut self.cli_agent_boundary_injected,
pending_background_ack: &mut self.pending_background_ack,
pending_external_action_ack: &mut self.pending_external_action_ack,
}
}
pub(in crate::agent) fn push_system_message(
&mut self,
directive: crate::agent::SystemDirective,
) {
self.pending_system_messages.push(directive);
}
pub(in crate::agent) fn system_message_count(&self) -> usize {
self.pending_system_messages.len()
}
pub(in crate::agent) fn take_system_messages(&mut self) -> Vec<crate::agent::SystemDirective> {
std::mem::take(&mut self.pending_system_messages)
}
pub(in crate::agent) fn pending_background_ack(&self) -> Option<&str> {
self.pending_background_ack.as_deref()
}
pub(in crate::agent) fn set_pending_background_ack(&mut self, ack: impl Into<String>) {
self.pending_background_ack = Some(ack.into());
}
pub(in crate::agent) fn take_pending_background_ack(&mut self) -> Option<String> {
self.pending_background_ack.take()
}
pub(in crate::agent) fn pending_external_action_ack(&self) -> Option<&str> {
self.pending_external_action_ack.as_deref()
}
pub(in crate::agent) fn set_pending_external_action_ack(&mut self, ack: impl Into<String>) {
self.pending_external_action_ack = Some(ack.into());
}
pub(in crate::agent) fn take_pending_external_action_ack(&mut self) -> Option<String> {
self.pending_external_action_ack.take()
}
pub(in crate::agent) fn identity_prefill_text(&self) -> Option<&str> {
self.identity_prefill_text.as_deref()
}
pub(in crate::agent) fn set_identity_prefill_text(&mut self, text: impl Into<String>) {
self.identity_prefill_text = Some(text.into());
}
pub(in crate::agent) fn take_identity_prefill_text(&mut self) -> Option<String> {
self.identity_prefill_text.take()
}
pub(in crate::agent) fn cli_agent_boundary_injected(&self) -> bool {
self.cli_agent_boundary_injected
}
pub(in crate::agent) fn set_cli_agent_boundary_injected(&mut self, injected: bool) {
self.cli_agent_boundary_injected = injected;
}
}
#[cfg(test)]
mod tests {
use super::PendingDirectives;
use crate::agent::SystemDirective;
#[test]
fn default_state_has_no_pending_directives() {
let state = PendingDirectives::default();
assert_eq!(state.system_message_count(), 0);
assert_eq!(state.pending_background_ack(), None);
assert_eq!(state.pending_external_action_ack(), None);
assert_eq!(state.identity_prefill_text(), None);
assert!(!state.cli_agent_boundary_injected());
}
#[test]
fn pushes_and_takes_system_directives() {
let mut state = PendingDirectives::default();
state.push_system_message(SystemDirective::RouteFailsafeActive);
state.push_system_message(SystemDirective::EvidenceGroundingRequired);
assert_eq!(state.system_message_count(), 2);
let taken = state.take_system_messages();
assert_eq!(taken.len(), 2);
assert_eq!(state.system_message_count(), 0);
}
#[test]
fn stores_and_takes_acknowledgements() {
let mut state = PendingDirectives::default();
state.set_pending_background_ack("background started");
state.set_pending_external_action_ack("external action complete");
assert_eq!(state.pending_background_ack(), Some("background started"));
assert_eq!(
state.pending_external_action_ack(),
Some("external action complete")
);
assert_eq!(
state.take_pending_background_ack(),
Some("background started".to_string())
);
assert_eq!(
state.take_pending_external_action_ack(),
Some("external action complete".to_string())
);
assert_eq!(state.pending_background_ack(), None);
assert_eq!(state.pending_external_action_ack(), None);
}
#[test]
fn stores_and_takes_identity_prefill() {
let mut state = PendingDirectives::default();
state.set_identity_prefill_text("prefill");
assert_eq!(state.identity_prefill_text(), Some("prefill"));
assert_eq!(
state.take_identity_prefill_text(),
Some("prefill".to_string())
);
assert_eq!(state.identity_prefill_text(), None);
}
#[test]
fn tracks_cli_agent_boundary_injection() {
let mut state = PendingDirectives::default();
state.set_cli_agent_boundary_injected(true);
assert!(state.cli_agent_boundary_injected());
state.set_cli_agent_boundary_injected(false);
assert!(!state.cli_agent_boundary_injected());
}
}