diff --git a/src/agent/agent_loop.rs b/src/agent/agent_loop.rs
index ae9ab041..71c4cba3 100644
--- a/src/agent/agent_loop.rs
+++ b/src/agent/agent_loop.rs
@@ -450,6 +450,14 @@ impl Agent {
None
};
+ // Eagerly initialize engine v2 so gateway API endpoints can serve
+ // data (projects, missions, threads) before the first chat message.
+ if crate::bridge::is_engine_v2_enabled()
+ && let Err(e) = crate::bridge::init_engine(&self).await
+ {
+ tracing::debug!("engine v2: eager init failed: {e}");
+ }
+
// Start channels
let mut message_stream = self.channels.start_all().await?;
diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs
index a2e94fde..6d19236a 100644
--- a/src/bridge/mod.rs
+++ b/src/bridge/mod.rs
@@ -22,6 +22,8 @@ pub use router::{
get_engine_mission,
get_engine_project,
get_engine_thread,
+ // Initialization
+ init_engine,
// Action handlers
handle_approval,
handle_clear,
diff --git a/src/bridge/router.rs b/src/bridge/router.rs
index b633f125..1ae2bfa0 100644
--- a/src/bridge/router.rs
+++ b/src/bridge/router.rs
@@ -84,7 +84,10 @@ enum PendingApprovalResolution {
}
/// Get or initialize the engine state using the agent's dependencies.
-async fn get_or_init_engine(agent: &Agent) -> Result<(), Error> {
+///
+/// Called eagerly at startup (from `Agent::run()`) when `ENGINE_V2=true`,
+/// and defensively from each handler as a lazy fallback.
+pub async fn init_engine(agent: &Agent) -> Result<(), Error> {
let lock = ENGINE_STATE.get_or_init(|| RwLock::new(None));
let guard = lock.read().await;
if guard.is_some() {
@@ -456,7 +459,7 @@ pub async fn handle_approval(
approved: bool,
always: bool,
) -> Result