use std::sync::Arc;
use async_trait::async_trait;
use regex::Regex;
use talon_core::Result;
use talon_core::channel::Channel;
use talon_core::protocol::{ContainerOutput, OutputStatus};
use talon_core::types::{NewMessage, RegisteredGroup};
use talon_router::{AgentOutputHandler, SessionCommandDeps};
use talon_scheduler::CloseTimer;
use super::Orchestrator;
use super::messages::trigger_ok;
use super::run::RunOptions;
pub struct LaneCommandDeps<'a> {
pub host: &'a Orchestrator,
pub jid: &'a str,
pub group: &'a RegisteredGroup,
pub channel: Arc<dyn Channel>,
pub pattern: &'a Regex,
}
struct CommandSink<'a> {
inner: &'a mut dyn AgentOutputHandler,
close: CloseTimer,
}
#[async_trait]
impl AgentOutputHandler for CommandSink<'_> {
async fn on_output(&mut self, output: &ContainerOutput) {
self.inner.on_output(output).await;
if output.status == OutputStatus::Success {
self.close.arm_once();
}
}
}
#[async_trait]
impl SessionCommandDeps for LaneCommandDeps<'_> {
async fn send_message(&self, text: &str) -> Result<()> {
self.host.send_outbound(self.jid, text).await.map(|_| ())
}
async fn set_typing(&self, typing: bool) {
if let Err(e) = self.channel.set_typing(self.jid, typing).await {
tracing::debug!(chat_jid = %self.jid, error = %e, "Failed to set typing indicator");
}
}
async fn run_agent(
&self,
prompt: &str,
on_output: &mut dyn AgentOutputHandler,
) -> OutputStatus {
let mut sink = CommandSink {
inner: on_output,
close: self
.host
.queue
.close_timer(self.jid, self.host.settings.command_close_delay),
};
let outcome = self
.host
.run_agent(
self.group,
prompt,
self.jid,
RunOptions::message(),
&mut sink,
)
.await;
sink.close.cancel();
outcome.status
}
fn close_stdin(&self) {
self.host.queue.close_stdin(self.jid);
}
fn advance_cursor(&self, timestamp: &str) {
self.host.set_cursor(self.jid, timestamp);
}
fn can_sender_interact(&self, message: &NewMessage) -> bool {
let is_main = self.group.is_main;
let requires_trigger = !is_main && self.group.requires_trigger != Some(false);
if is_main || !requires_trigger {
return true;
}
let allowlist = self.host.allowlist.current();
trigger_ok(self.jid, self.pattern, &allowlist, message)
}
fn clear_session(&self) {
self.host.clear_session_for(self.group);
tracing::info!(chat_jid = %self.jid, group = %self.group.name, "Session cleared");
}
}