Track Guardian reviewer turn and tool analytics (#41023)

## Why

Trusted Guardian reviewer sessions can emit events without an app-server listener, leaving their turns and tool usage absent from analytics.

## What changed

- Translate Guardian session lifecycle and canonical tool item events into analytics notifications while excluding messages, reasoning, and review content.
- Preserve Guardian lineage during prewarming even when inherited client metadata is not yet available, without emitting an incomplete thread initialization event.
- Limit direct session tracking to trusted Guardian children so resumed threads that have an app-server listener do not report duplicate events.

## Testing

- Cover completed, failed, and interrupted Guardian turns, tool counts and timing, lineage, content exclusion, prewarmed lineage, and resumed-thread deduplication.

GitOrigin-RevId: 70ace5f7ed30a7e3b6a9d6097c8b2a6a326dc4f7
This commit is contained in:
marksteinbrick-oai authored and copyberry committed 2026-08-27 02:19:10 +00:00
1 parent 81e180044d
commit 307ce6cda9
10 files changed
+467 -28

No files matched your search

+22 -2
View File
@@ -1993,6 +1993,24 @@ impl Session {
id: turn_context.sub_id.clone(),
msg,
};
// Private reviewers have no app-server listener; publicly resumed Guardian threads do.
if matches!(
&turn_context.session_source,
SessionSource::SubAgent(SubAgentSource::Other(name))
if name == crate::guardian::GUARDIAN_REVIEWER_NAME
) && self.services.analytics_events_client.is_enabled()
&& turn_context.parent_thread_id.is_some()
&& self
.state
.lock()
.await
.session_configuration
.trusted_guardian_reviewer
{
self.services
.analytics_events_client
.track_guardian_session_event(self.thread_id, &event);
}
self.send_event_raw(event).await;
self.maybe_notify_parent_of_terminal_turn(turn_context, &legacy_source)
.await;
@@ -4329,10 +4347,12 @@ pub(crate) fn emit_subagent_session_started(
client_name,
client_version,
} = client_metadata;
let (Some(client_name), Some(client_version)) = (client_name, client_version) else {
if (client_name.is_none() || client_version.is_none())
&& subagent_source.kind() != crate::guardian::GUARDIAN_REVIEWER_NAME
{
tracing::warn!("skipping subagent thread analytics: missing inherited client metadata");
return;
};
}
let created_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()