Add Guardian v2 approval review metrics (#39224)

## What changed

- Pass metrics bound to the active turn's model to approval-review contributors.
- Add counter support to `ExtensionMetrics` while preserving host session attribution.
- Record Guardian v2 tool-call score lag and count stale-score fallbacks with the `score_lag` reason.

## Testing

- Cover counter forwarding and session metadata tags.
- Verify Guardian v2 metric samples for current and stale scores.

GitOrigin-RevId: 0ec34736d29d2e37ed217a27e773b4f6230ae38a
This commit is contained in:
Dylan Hurd authored and copyberry committed 2026-08-18 16:27:54 +00:00
1 parent 2a30972fcb
commit e7e13c68e2
15 files changed
+230 -16

No files matched your search

@@ -3,6 +3,9 @@
/// Implementations are expected to attach the host's session attribution before
/// forwarding samples to the configured metrics backend.
pub trait ExtensionMetrics: Send + Sync {
/// Increments a counter with optional extension-provided tags.
fn counter(&self, name: &str, inc: i64, tags: &[(&str, &str)]);
/// Records one histogram sample with optional extension-provided tags.
fn histogram(&self, name: &str, value: i64, tags: &[(&str, &str)]);
}
@@ -318,11 +318,13 @@ pub trait ToolLifecycleContributor: Send + Sync {
/// Extension contribution that can claim rendered approval-review prompts.
pub trait ApprovalReviewContributor: Send + Sync {
/// Reviews one action using metrics bound to the active turn's model.
fn contribute<'a>(
&'a self,
session_store: &'a ExtensionData,
thread_store: &'a ExtensionData,
prompt: &'a str,
extension_metrics: Option<Arc<dyn ExtensionMetrics>>,
) -> ExtensionFuture<'a, Option<ReviewDecision>>;
}
+8 -1
View File
@@ -7,6 +7,7 @@ use crate::ConfigContributor;
use crate::ContextContributor;
use crate::ExtensionData;
use crate::ExtensionEventSink;
use crate::ExtensionMetrics;
use crate::McpServerContributor;
use crate::NoopExtensionEventSink;
use crate::SkillInvocationContributor;
@@ -194,10 +195,16 @@ impl<C: Sync> ExtensionRegistry<C> {
session_store: &ExtensionData,
thread_store: &ExtensionData,
prompt: &str,
extension_metrics: Option<Arc<dyn ExtensionMetrics>>,
) -> Option<ReviewDecision> {
for contributor in &self.approval_review_contributors {
if let Some(decision) = contributor
.contribute(session_store, thread_store, prompt)
.contribute(
session_store,
thread_store,
prompt,
extension_metrics.clone(),
)
.await
{
return Some(decision);