#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::sync::Arc;
use std::sync::Mutex;
use aion_core::{
ActivityEvent, ActivityEventKind, ActivityId, ContentType, InterventionCapabilities,
InterventionCommand, InterventionKind, InterventionPrimitive, MessageRole, Payload, WorkflowId,
};
use aion_integrations::contract::{AgentHarness, AgentSession};
use aion_integrations::error::HarnessError;
use aion_integrations::spec::AgentRunSpec;
use async_trait::async_trait;
use chrono::Utc;
use futures::stream::BoxStream;
use tokio::sync::mpsc;
use super::{ControlMessage, ControlReceiver, harness_error_to_outcome, spawn_agent};
use crate::runtime::loop_::DispatchOutcome;
struct FakeSession {
capabilities: InterventionCapabilities,
events: Option<Vec<ActivityEvent>>,
interventions: Arc<Mutex<Vec<InterventionKind>>>,
result: Payload,
fail_result: Option<HarnessError>,
}
struct FakeHarness {
session: Mutex<Option<FakeSession>>,
}
impl FakeHarness {
fn new(session: FakeSession) -> Self {
Self {
session: Mutex::new(Some(session)),
}
}
}
#[async_trait]
impl AgentHarness for FakeHarness {
type Session = FakeSession;
async fn start(&self, _spec: AgentRunSpec) -> Result<Self::Session, HarnessError> {
self.session
.lock()
.unwrap()
.take()
.ok_or_else(|| HarnessError::transport("fake harness started twice"))
}
}
#[async_trait]
impl AgentSession for FakeSession {
fn capabilities(&self) -> &InterventionCapabilities {
&self.capabilities
}
fn events(&mut self) -> BoxStream<'static, ActivityEvent> {
let batch = self.events.take().unwrap_or_default();
Box::pin(futures::stream::iter(batch))
}
async fn intervene(&self, cmd: InterventionCommand) -> Result<(), HarnessError> {
if !self.capabilities.supports(&cmd.kind) {
return Err(HarnessError::capability_not_supported(format!(
"{:?}",
cmd.kind.primitive()
)));
}
self.interventions.lock().unwrap().push(cmd.kind);
Ok(())
}
async fn wait_result(self) -> Result<Payload, HarnessError> {
match self.fail_result {
Some(error) => Err(error),
None => Ok(self.result),
}
}
}
fn message_event(text: &str) -> ActivityEvent {
ActivityEvent {
workflow_id: WorkflowId::new(uuid::Uuid::nil()),
activity_id: ActivityId::from_sequence_position(2),
attempt: 1,
agent_id: uuid::Uuid::nil(),
agent_role: "root".to_owned(),
emitted_at: Utc::now(),
worker_seq: 0,
store_seq: None,
ephemeral: false,
kind: ActivityEventKind::Message {
role: MessageRole::Assistant,
text: text.to_owned(),
},
}
}
fn spec() -> AgentRunSpec {
AgentRunSpec::new(
WorkflowId::new(uuid::Uuid::nil()),
ActivityId::from_sequence_position(2),
1,
"agent-activity",
Payload::new(ContentType::Json, b"\"in\"".to_vec()),
)
}
fn inject_command() -> InterventionCommand {
InterventionCommand {
workflow_id: WorkflowId::new(uuid::Uuid::nil()),
activity_id: ActivityId::from_sequence_position(2),
attempt: 1,
issued_by: Some("operator".to_owned()),
issued_at: Utc::now(),
kind: InterventionKind::InjectMessage {
text: "steer".to_owned(),
priority: aion_core::InjectPriority::Interrupt,
},
}
}
fn caps_inject_cancel() -> InterventionCapabilities {
InterventionCapabilities::from_primitives([
InterventionPrimitive::InjectMessage,
InterventionPrimitive::Cancel,
])
}
#[tokio::test]
async fn drives_events_to_sink_and_captures_the_terminal_result() {
let session = FakeSession {
capabilities: caps_inject_cancel(),
events: Some(vec![message_event("working"), message_event("done")]),
interventions: Arc::new(Mutex::new(Vec::new())),
result: Payload::new(ContentType::Json, b"{\"ok\":true}".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, mut event_rx) = mpsc::unbounded_channel();
let outcome = spawn_agent(&harness, spec(), event_tx, None)
.await
.expect("driver runs to a terminal result");
let first = event_rx.recv().await.expect("first event forwarded");
let second = event_rx.recv().await.expect("second event forwarded");
assert!(matches!(
first.kind,
ActivityEventKind::Message { ref text, .. } if text == "working"
));
assert!(matches!(
second.kind,
ActivityEventKind::Message { ref text, .. } if text == "done"
));
assert!(event_rx.recv().await.is_none(), "sink closes after run");
match outcome {
DispatchOutcome::Completed { output } => {
assert_eq!(output.content_type(), &ContentType::Json);
assert_eq!(output.bytes(), b"{\"ok\":true}");
}
DispatchOutcome::Failed { failure } => panic!("expected completion, got {failure:?}"),
}
}
#[tokio::test]
async fn feeds_control_commands_into_intervene() {
let interventions = Arc::new(Mutex::new(Vec::new()));
let session = FakeSession {
capabilities: caps_inject_cancel(),
events: Some(Vec::new()),
interventions: Arc::clone(&interventions),
result: Payload::new(ContentType::Json, b"null".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, _event_rx) = mpsc::unbounded_channel();
let (control_tx, control_rx): (_, ControlReceiver) = mpsc::unbounded_channel();
control_tx
.send(ControlMessage::new(inject_command()))
.unwrap();
drop(control_tx);
let outcome = spawn_agent(&harness, spec(), event_tx, Some(control_rx))
.await
.expect("driver runs to a terminal result");
let recorded = interventions.lock().unwrap();
assert_eq!(recorded.len(), 1, "the queued command reached intervene");
assert!(matches!(
recorded[0],
InterventionKind::InjectMessage { .. }
));
assert!(matches!(outcome, DispatchOutcome::Completed { .. }));
}
#[tokio::test]
async fn a_command_with_an_ack_channel_replies_the_neutral_outcome() {
use aion_core::InterventionOutcome;
use tokio::sync::oneshot;
let interventions = Arc::new(Mutex::new(Vec::new()));
let session = FakeSession {
capabilities: caps_inject_cancel(),
events: Some(Vec::new()),
interventions: Arc::clone(&interventions),
result: Payload::new(ContentType::Json, b"null".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, _event_rx) = mpsc::unbounded_channel();
let (control_tx, control_rx): (_, ControlReceiver) = mpsc::unbounded_channel();
let (ack_ok_tx, ack_ok_rx) = oneshot::channel();
control_tx
.send(ControlMessage::with_ack(inject_command(), ack_ok_tx))
.unwrap();
let mut gated = inject_command();
gated.kind = InterventionKind::PauseResume { paused: true };
let (ack_gated_tx, ack_gated_rx) = oneshot::channel();
control_tx
.send(ControlMessage::with_ack(gated, ack_gated_tx))
.unwrap();
drop(control_tx);
spawn_agent(&harness, spec(), event_tx, Some(control_rx))
.await
.expect("driver runs to a terminal result");
assert_eq!(
ack_ok_rx.await.expect("applied ack delivered"),
InterventionOutcome::Applied
);
assert!(matches!(
ack_gated_rx.await.expect("gated ack delivered"),
InterventionOutcome::CapabilityNotSupported { .. }
));
assert_eq!(interventions.lock().unwrap().len(), 1);
}
#[tokio::test]
async fn a_capability_gated_command_is_not_fatal() {
let session = FakeSession {
capabilities: InterventionCapabilities::from_primitives([
InterventionPrimitive::InjectMessage,
]),
events: Some(Vec::new()),
interventions: Arc::new(Mutex::new(Vec::new())),
result: Payload::new(ContentType::Json, b"null".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, _event_rx) = mpsc::unbounded_channel();
let (control_tx, control_rx): (_, ControlReceiver) = mpsc::unbounded_channel();
let mut gated = inject_command();
gated.kind = InterventionKind::PauseResume { paused: true };
control_tx.send(ControlMessage::new(gated)).unwrap();
drop(control_tx);
let outcome = spawn_agent(&harness, spec(), event_tx, Some(control_rx))
.await
.expect("a gated command does not fail the run");
assert!(matches!(outcome, DispatchOutcome::Completed { .. }));
}
#[tokio::test]
async fn observability_only_session_runs_with_no_control_channel() {
let session = FakeSession {
capabilities: InterventionCapabilities::none(),
events: Some(vec![message_event("only watching")]),
interventions: Arc::new(Mutex::new(Vec::new())),
result: Payload::new(ContentType::Json, b"\"ok\"".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, mut event_rx) = mpsc::unbounded_channel();
let outcome = spawn_agent(&harness, spec(), event_tx, None)
.await
.expect("observability-only run completes");
assert!(event_rx.recv().await.is_some(), "event still streamed");
assert!(matches!(outcome, DispatchOutcome::Completed { .. }));
}
#[tokio::test]
async fn a_closed_event_sink_still_reaches_the_result() {
let session = FakeSession {
capabilities: InterventionCapabilities::none(),
events: Some(vec![message_event("dropped")]),
interventions: Arc::new(Mutex::new(Vec::new())),
result: Payload::new(ContentType::Json, b"\"ok\"".to_vec()),
fail_result: None,
};
let harness = FakeHarness::new(session);
let (event_tx, event_rx) = mpsc::unbounded_channel();
drop(event_rx);
let outcome = spawn_agent(&harness, spec(), event_tx, None)
.await
.expect("a closed sink does not fail the run");
assert!(matches!(outcome, DispatchOutcome::Completed { .. }));
}
#[tokio::test]
async fn a_harness_reported_failure_surfaces_and_maps_to_failed() {
let session = FakeSession {
capabilities: InterventionCapabilities::none(),
events: Some(Vec::new()),
interventions: Arc::new(Mutex::new(Vec::new())),
result: Payload::new(ContentType::Json, b"null".to_vec()),
fail_result: Some(HarnessError::harness("exit code 1")),
};
let harness = FakeHarness::new(session);
let (event_tx, _event_rx) = mpsc::unbounded_channel();
let error = spawn_agent(&harness, spec(), event_tx, None)
.await
.expect_err("a harness-reported failure surfaces");
assert!(matches!(error, HarnessError::Harness { .. }));
match harness_error_to_outcome(&error) {
DispatchOutcome::Failed { failure } => assert!(failure.is_retryable()),
DispatchOutcome::Completed { .. } => panic!("expected a Failed outcome"),
}
}