use std::rc::Rc;
use crate::agent_events::AgentEvent;
use crate::value::VmError;
use super::super::helpers::transcript_event;
use super::helpers::{
append_host_messages_to_recorded, append_message_to_contexts, build_agent_system_prompt,
inject_queued_user_messages, runtime_feedback_message,
};
use super::state::AgentLoopState;
use super::{drain_pending_feedback, emit_agent_event};
pub(super) struct PreflightContext<'a> {
pub bridge: &'a Option<Rc<crate::bridge::HostBridge>>,
pub session_id: &'a str,
pub resumed_iterations: usize,
pub iteration: usize,
pub base_system: Option<&'a str>,
pub tool_contract_prompt: Option<&'a str>,
pub persistent_system_prompt: Option<&'a str>,
}
pub(super) async fn run_turn_preflight(
state: &mut AgentLoopState,
opts: &mut super::super::api::LlmCallOptions,
ctx: PreflightContext<'_>,
) -> Result<(), VmError> {
state.total_iterations = ctx.resumed_iterations + ctx.iteration + 1;
crate::llm::agent_observe::set_current_iteration(Some(state.total_iterations));
state.daemon_state = "active".to_string();
let immediate_messages = inject_queued_user_messages(
ctx.bridge.as_ref(),
&mut state.visible_messages,
crate::bridge::DeliveryCheckpoint::InterruptImmediate,
)
.await?;
append_host_messages_to_recorded(&mut state.recorded_messages, &immediate_messages);
for message in &immediate_messages {
state.transcript_events.push(transcript_event(
"host_input",
"user",
"public",
&message.content,
Some(serde_json::json!({"delivery": format!("{:?}", message.mode)})),
));
}
if !immediate_messages.is_empty() {
state.consecutive_text_only = 0;
state.idle_backoff_ms = 100;
}
let default_system = build_agent_system_prompt(
ctx.base_system,
ctx.tool_contract_prompt,
ctx.persistent_system_prompt,
);
let mut call_messages = state.visible_messages.clone();
let call_system = default_system;
emit_agent_event(&AgentEvent::TurnStart {
session_id: ctx.session_id.to_string(),
iteration: ctx.iteration,
})
.await;
for (kind, content) in drain_pending_feedback(ctx.session_id) {
emit_agent_event(&AgentEvent::FeedbackInjected {
session_id: ctx.session_id.to_string(),
kind: kind.clone(),
content: content.clone(),
})
.await;
append_message_to_contexts(
&mut state.visible_messages,
&mut state.recorded_messages,
runtime_feedback_message(&kind, content),
);
call_messages = state.visible_messages.clone();
}
let ledger_rendered = state.task_ledger.render_for_prompt();
if !ledger_rendered.is_empty() {
call_messages.push(serde_json::json!({
"role": "user",
"content": format!(
"<runtime_injection kind=\"task_ledger\">\n{ledger_rendered}\n</runtime_injection>"
),
}));
}
crate::llm::api::debug_log_message_shapes(
&format!("agent iteration={} preflight", ctx.iteration),
&call_messages,
);
opts.messages = call_messages;
opts.system = call_system;
Ok(())
}