From 3a381002198edd2d46b5daaae1639f97c0b8bdeb Mon Sep 17 00:00:00 2001 From: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Date: Thu, 9 Jul 2026 08:12:03 +0530 Subject: [PATCH] refactor(triage): collapse dispatch_target_agent onto build_root_parent (#4369) (#4402) Co-authored-by: Steven Enamakel --- src/openhuman/agent/triage/escalation.rs | 104 ++++++++++++----------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/openhuman/agent/triage/escalation.rs b/src/openhuman/agent/triage/escalation.rs index 1517faff3..ea125a114 100644 --- a/src/openhuman/agent/triage/escalation.rs +++ b/src/openhuman/agent/triage/escalation.rs @@ -16,14 +16,12 @@ //! installed on the task-local so [`run_subagent`] can inherit the //! provider and tools. -use std::sync::Arc; - use anyhow::{anyhow, Context}; use crate::openhuman::agent::harness::definition::AgentDefinitionRegistry; use crate::openhuman::agent::harness::fork_context::{with_parent_context, ParentExecutionContext}; use crate::openhuman::agent::harness::subagent_runner::{self, SubagentRunOptions}; -use crate::openhuman::agent::Agent; +use crate::openhuman::agent_orchestration::parent_context::build_root_parent; use crate::openhuman::config::Config; use super::decision::TriageAction; @@ -240,6 +238,28 @@ pub async fn apply_decision(run: TriageRun, envelope: &TriggerEnvelope) -> anyho /// normally needs. The cost is acceptable because `react`/`escalate` /// triggers are relatively rare (most triggers are `drop`/`acknowledge`) /// and the construction is the same O(1) code path `agent_chat` uses. +/// Build the triage root parent: the shared [`build_root_parent`] context with +/// nested spawns scoped to the single dispatched target agent. +/// +/// This collapses the previously hand-rolled ~20-field [`ParentExecutionContext`] +/// literal onto the shared builder so the two can't drift (#4369). The identity +/// fields come straight from `build_root_parent` and match the old literal +/// exactly: `agent_definition_id`/`channel` = `"triage"`, `session_id` = +/// `"triage-{uuid}"`, and the session-key chain + PFormat tool-call format are +/// inherited from the config-built agent. The **only** field triage overrides is +/// `allowed_subagent_ids`: the escalated agent may itself nested-spawn only the +/// dispatched target (the builder defaults this to empty for background roots). +async fn build_triage_parent( + config: &Config, + agent_id: &str, +) -> anyhow::Result { + let mut parent_ctx = build_root_parent(config, "triage", "triage", "triage") + .await + .context("building root parent for sub-agent dispatch")?; + parent_ctx.allowed_subagent_ids = [agent_id.to_string()].into_iter().collect(); + Ok(parent_ctx) +} + async fn dispatch_target_agent(agent_id: &str, prompt: &str) -> anyhow::Result { #[cfg(test)] if agent_id.starts_with("missing-agent-") { @@ -252,58 +272,16 @@ async fn dispatch_target_agent(agent_id: &str, prompt: &str) -> anyhow::Result = tokio::sync::Mutex::const_new(()); struct TestEventsGuard(tokio::sync::MutexGuard<'static, ()>);