Skip to content

Commit 18f77e2

Browse files
committed
fix(api): harden live tool-output ordering and liveness tracking
1 parent 49ee688 commit 18f77e2

3 files changed

Lines changed: 241 additions & 33 deletions

File tree

interface/src/api/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ export interface ToolOutputEvent {
204204
type: "tool_output";
205205
agent_id: string;
206206
channel_id: string | null;
207-
process_type: string;
207+
process_type: ProcessType;
208208
process_id: string;
209209
/** Stable identifier matching the tool_call that initiated this stream. */
210210
call_id: string;

src/agent/cortex.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,8 +1590,15 @@ pub fn spawn_cortex_loop(deps: AgentDeps, logger: CortexLogger) -> tokio::task::
15901590
let cortex = Cortex::new(deps.clone(), system_prompt);
15911591
let mut event_rx = deps.event_tx.subscribe();
15921592
let mut memory_event_rx = deps.memory_event_tx.subscribe();
1593-
if let Err(error) =
1594-
run_cortex_loop(&cortex, &logger, &mut event_rx, &mut memory_event_rx).await
1593+
let mut tool_output_rx = deps.tool_output_tx.subscribe();
1594+
if let Err(error) = run_cortex_loop(
1595+
&cortex,
1596+
&logger,
1597+
&mut event_rx,
1598+
&mut memory_event_rx,
1599+
&mut tool_output_rx,
1600+
)
1601+
.await
15951602
{
15961603
tracing::error!(%error, "cortex loop exited with error");
15971604
}
@@ -1795,6 +1802,7 @@ async fn run_cortex_loop(
17951802
logger: &CortexLogger,
17961803
event_rx: &mut broadcast::Receiver<ProcessEvent>,
17971804
memory_event_rx: &mut broadcast::Receiver<ProcessEvent>,
1805+
tool_output_rx: &mut broadcast::Receiver<ProcessEvent>,
17981806
) -> anyhow::Result<()> {
17991807
tracing::info!("cortex loop started");
18001808

@@ -1853,7 +1861,10 @@ async fn run_cortex_loop(
18531861
let mut last_lag_warning_control: Option<Instant> = None;
18541862
let mut lagged_since_last_warning_memory: u64 = 0;
18551863
let mut last_lag_warning_memory: Option<Instant> = None;
1864+
let mut lagged_since_last_warning_tool_output: u64 = 0;
1865+
let mut last_lag_warning_tool_output: Option<Instant> = None;
18561866
let mut memory_event_stream_open = true;
1867+
let mut tool_output_stream_open = true;
18571868
let mut refresh_task: Option<tokio::task::JoinHandle<BulletinRefreshOutcome>> = None;
18581869
let mut maintenance_task: Option<
18591870
tokio::task::JoinHandle<crate::error::Result<memory_maintenance::MaintenanceReport>>,
@@ -1937,6 +1948,31 @@ async fn run_cortex_loop(
19371948
}
19381949
}
19391950
},
1951+
event = tool_output_rx.recv(), if tool_output_stream_open => {
1952+
match handle_cortex_receiver_result(
1953+
event,
1954+
"tool_output",
1955+
ReceiverClosedBehavior::DisableStream,
1956+
&mut lagged_since_last_warning_tool_output,
1957+
&mut last_lag_warning_tool_output,
1958+
LAG_WARNING_INTERVAL_SECS,
1959+
) {
1960+
CortexReceiverOutcome::Observe(event) => cortex.observe(event).await,
1961+
CortexReceiverOutcome::Lagged { dropped } => {
1962+
#[cfg(feature = "metrics")]
1963+
crate::telemetry::Metrics::global()
1964+
.event_receiver_lagged_events_total
1965+
.with_label_values(&[&*cortex.deps.agent_id, "cortex_tool_output"])
1966+
.inc_by(dropped);
1967+
#[cfg(not(feature = "metrics"))]
1968+
let _ = dropped;
1969+
}
1970+
CortexReceiverOutcome::StopLoop => unreachable!("tool output stream cannot stop cortex loop"),
1971+
CortexReceiverOutcome::DisableStream => {
1972+
tool_output_stream_open = false;
1973+
}
1974+
}
1975+
},
19401976
_ = tick_timer.tick() => {
19411977
if let Err(error) = cortex.run_health_tick(logger).await {
19421978
tracing::warn!(%error, "cortex health tick failed");

0 commit comments

Comments
 (0)