fix(engine): eagerly initialize engine v2 at startup

The gateway API endpoints (/api/engine/missions, etc.) call bridge
query functions that return empty results when the engine state hasn't
been initialized yet. Previously, initialization only happened lazily
on the first chat message via handle_with_engine().

Now when ENGINE_V2=true, the engine is initialized in Agent::run()
before channels start, so the self-improvement mission and other
engine state is available to gateway API endpoints immediately.

Also rename get_or_init_engine → init_engine and make it public so
it can be called from agent_loop.rs at startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
2026-03-26 19:07:35 -07:00
co-authored by Claude Opus 4.6
parent 7c153d584b
commit 6d56d9cb11
3 changed files with 19 additions and 6 deletions
+8
View File
@@ -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?;
+2
View File
@@ -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,
+9 -6
View File
@@ -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<Option<String>, Error> {
get_or_init_engine(agent).await?;
init_engine(agent).await?;
let lock = ENGINE_STATE
.get()
@@ -497,7 +500,7 @@ pub async fn handle_exec_approval(
approved: bool,
always: bool,
) -> Result<Option<String>, Error> {
get_or_init_engine(agent).await?;
init_engine(agent).await?;
let lock = ENGINE_STATE
.get()
@@ -642,7 +645,7 @@ pub async fn handle_interrupt(
agent: &Agent,
message: &IncomingMessage,
) -> Result<Option<String>, Error> {
get_or_init_engine(agent).await?;
init_engine(agent).await?;
let lock = ENGINE_STATE
.get()
@@ -703,7 +706,7 @@ pub async fn handle_clear(
/// Stop all active threads and clear conversation entries.
async fn clear_engine_conversation(agent: &Agent, message: &IncomingMessage) -> Result<(), Error> {
get_or_init_engine(agent).await?;
init_engine(agent).await?;
let lock = ENGINE_STATE
.get()
@@ -758,7 +761,7 @@ pub async fn handle_with_engine(
content: &str,
) -> Result<Option<String>, Error> {
// Ensure engine is initialized
get_or_init_engine(agent).await?;
init_engine(agent).await?;
let lock = ENGINE_STATE
.get()