feat(engine): Phase 6 — bridge adapters for main crate integration

Strategy C parallel deployment: when ENGINE_V2=true env var is set,
user messages route through the engine instead of the existing agentic
loop. All existing behavior is unchanged when the flag is off.

Bridge module (src/bridge/):
- LlmBridgeAdapter: wraps LlmProvider as engine LlmBackend, converts
  ThreadMessage↔ChatMessage, ActionDef↔ToolDefinition, depth-based
  model routing (primary vs cheap_llm)
- EffectBridgeAdapter: wraps ToolRegistry+SafetyLayer as EffectExecutor,
  routes tool calls through existing execute_tool_with_safety pipeline
- InMemoryStore: HashMap-backed Store impl (no DB tables needed yet)
- EngineRouter: is_engine_v2_enabled() + handle_with_engine() that
  builds engine from Agent deps and processes messages end-to-end

Integration touchpoint (4 lines in agent_loop.rs):
  After hook processing, before session resolution, check ENGINE_V2
  flag and route UserInput through the engine path.

Accessor visibility widened: llm(), cheap_llm(), safety(), tools()
changed from pub(super) to pub(crate) for bridge access.

85 engine tests + main crate clippy clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
2026-03-22 00:22:13 -07:00
co-authored by Claude Opus 4.6
parent f0295f304f
commit ac4ced02ae
9 changed files with 622 additions and 4 deletions
+11 -4
View File
@@ -283,20 +283,20 @@ impl Agent {
self.deps.store.as_ref()
}
pub(super) fn llm(&self) -> &Arc<dyn LlmProvider> {
pub(crate) fn llm(&self) -> &Arc<dyn LlmProvider> {
&self.deps.llm
}
/// Get the cheap/fast LLM provider, falling back to the main one.
pub(super) fn cheap_llm(&self) -> &Arc<dyn LlmProvider> {
pub(crate) fn cheap_llm(&self) -> &Arc<dyn LlmProvider> {
self.deps.cheap_llm.as_ref().unwrap_or(&self.deps.llm)
}
pub(super) fn safety(&self) -> &Arc<SafetyLayer> {
pub(crate) fn safety(&self) -> &Arc<SafetyLayer> {
&self.deps.safety
}
pub(super) fn tools(&self) -> &Arc<ToolRegistry> {
pub(crate) fn tools(&self) -> &Arc<ToolRegistry> {
&self.deps.tools
}
@@ -1003,6 +1003,13 @@ impl Agent {
}
}
// Engine V2 routing (Strategy C: parallel deployment)
if let Submission::UserInput { ref content } = submission
&& crate::bridge::is_engine_v2_enabled()
{
return crate::bridge::handle_with_engine(self, message, content).await;
}
// Hydrate thread from DB if it's a historical thread not in memory
if let Some(external_thread_id) = message.conversation_scope() {
tracing::trace!(