Collect metrics from plugin shell commands (#38252)

## What changed

- Provide matching local plugin commands with a sandbox-writable temporary output file through `CODEX_PLUGIN_METRICS_OUTPUT` when analytics is enabled.
- Validate successful command output against the plugin's `analytics.yaml` declaration, including measurement names, enum dimensions, finite values, duplicate rows, and size limits, before publishing analytics events.
- Keep the output path reserved from user overrides and clean up the temporary file after execution.

## Testing

- Cover output validation, limits, cleanup, sandbox permissions, environment handling, and path replacement.
- Verify measurement collection through both classic and zsh-fork shell runtimes.

GitOrigin-RevId: 88af0f87dc2f207fcbcca6af498f5c940d79349d
This commit is contained in:
Kyle Brown
2026-08-12 21:23:21 +00:00
committed by copyberry
parent 8bb8d60234
commit 9ca0337dbf
15 changed files with 610 additions and 40 deletions
+32
View File
@@ -0,0 +1,32 @@
use crate::session::session::Session;
use crate::session::turn_context::TurnContext;
use codex_analytics::PluginMeasurementsInput;
use codex_core_plugins::PluginMetricsSidecar;
/// Finishes a metrics sidecar and publishes any valid rows.
pub(crate) fn finish_and_track_measurements(
metrics_sidecar: Option<PluginMetricsSidecar>,
exit_code: i32,
session: &Session,
turn: &TurnContext,
item_id: &str,
) {
let Some(metrics_sidecar) = metrics_sidecar else {
return;
};
let Some(batch) = metrics_sidecar.finish(exit_code) else {
return;
};
session
.services
.analytics_events_client
.track_plugin_measurements(PluginMeasurementsInput {
thread_id: session.thread_id().to_string(),
turn_id: turn.sub_id.clone(),
item_id: item_id.to_string(),
plugin_id: batch.plugin_id,
execution_id: batch.execution_id,
operation: batch.operation,
rows: batch.rows,
});
}
+1
View File
@@ -1,6 +1,7 @@
mod discoverable;
mod injection;
mod mentions;
pub(crate) mod metrics;
mod render;
#[cfg(test)]
#[path = "skill_snapshot_tests.rs"]