mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
Use request originator for analytics attribution
This commit is contained in:
@@ -155,6 +155,10 @@ use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
fn default_product_client_id() -> String {
|
||||
Originator::process_default().value().to_string()
|
||||
}
|
||||
|
||||
fn sample_thread_with_metadata(
|
||||
thread_id: &str,
|
||||
ephemeral: bool,
|
||||
@@ -941,6 +945,7 @@ fn app_mentioned_event_serializes_expected_shape() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: "codex_ios".to_string(),
|
||||
};
|
||||
let event = TrackEventRequest::AppMentioned(CodexAppMentionedEventRequest {
|
||||
event_type: "codex_app_mentioned",
|
||||
@@ -965,7 +970,7 @@ fn app_mentioned_event_serializes_expected_shape() {
|
||||
"thread_id": "thread-1",
|
||||
"turn_id": "turn-1",
|
||||
"app_name": "Calendar",
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"product_client_id": "codex_ios",
|
||||
"invoke_type": "explicit",
|
||||
"model_slug": "gpt-5"
|
||||
}
|
||||
@@ -979,6 +984,7 @@ fn app_used_event_serializes_expected_shape() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-2".to_string(),
|
||||
turn_id: "turn-2".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let event = TrackEventRequest::AppUsed(CodexAppUsedEventRequest {
|
||||
event_type: "codex_app_used",
|
||||
@@ -1280,11 +1286,13 @@ fn app_used_dedupe_is_keyed_by_turn_and_connector() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let turn_2 = TrackEventsContext {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-2".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
|
||||
assert_eq!(queue.should_enqueue_app_used(&turn_1, &app), true);
|
||||
@@ -2755,6 +2763,7 @@ fn plugin_used_event_serializes_expected_shape() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-3".to_string(),
|
||||
turn_id: "turn-3".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let event = TrackEventRequest::PluginUsed(CodexPluginUsedEventRequest {
|
||||
event_type: "codex_plugin_used",
|
||||
@@ -2785,9 +2794,10 @@ fn plugin_used_event_serializes_expected_shape() {
|
||||
|
||||
#[test]
|
||||
fn plugin_management_event_serializes_expected_shape() {
|
||||
let product_client_id = "codex_desktop".to_string();
|
||||
let event = TrackEventRequest::PluginInstalled(CodexPluginEventRequest {
|
||||
event_type: "codex_plugin_installed",
|
||||
event_params: codex_plugin_metadata(sample_plugin_metadata()),
|
||||
event_params: codex_plugin_metadata(sample_plugin_metadata(), product_client_id),
|
||||
});
|
||||
|
||||
let payload = serde_json::to_value(&event).expect("serialize plugin installed event");
|
||||
@@ -2803,7 +2813,7 @@ fn plugin_management_event_serializes_expected_shape() {
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": Originator::process_default().value().to_string()
|
||||
"product_client_id": "codex_desktop"
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -2815,7 +2825,7 @@ fn plugin_management_event_can_use_remote_plugin_id_override() {
|
||||
plugin.remote_plugin_id = Some("plugins~Plugin_remote".to_string());
|
||||
let event = TrackEventRequest::PluginInstalled(CodexPluginEventRequest {
|
||||
event_type: "codex_plugin_installed",
|
||||
event_params: codex_plugin_metadata(plugin),
|
||||
event_params: codex_plugin_metadata(plugin, default_product_client_id()),
|
||||
});
|
||||
|
||||
let payload = serde_json::to_value(&event).expect("serialize plugin installed event");
|
||||
@@ -2834,6 +2844,7 @@ fn hook_run_event_serializes_expected_shape() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-3".to_string(),
|
||||
turn_id: "turn-3".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let event = TrackEventRequest::HookRun(CodexHookRunEventRequest {
|
||||
event_type: "codex_hook_run",
|
||||
@@ -2871,6 +2882,7 @@ fn hook_run_metadata_maps_sources_and_statuses() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
|
||||
let system = serde_json::to_value(codex_hook_run_metadata(
|
||||
@@ -2926,6 +2938,7 @@ fn hook_run_metadata_maps_stopped_status() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: "codex_web".to_string(),
|
||||
};
|
||||
|
||||
let stopped = serde_json::to_value(codex_hook_run_metadata(
|
||||
@@ -2956,11 +2969,13 @@ fn plugin_used_dedupe_is_keyed_by_turn_and_plugin() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let turn_2 = TrackEventsContext {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-2".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
|
||||
assert_eq!(queue.should_enqueue_plugin_used(&turn_1, &plugin), true);
|
||||
@@ -2976,6 +2991,7 @@ async fn reducer_ingests_skill_invoked_fact() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let skill_path = PathBuf::from("/Users/abc/.codex/skills/doc/SKILL.md");
|
||||
let expected_skill_id = skill_id_for_local_skill(
|
||||
@@ -3009,7 +3025,7 @@ async fn reducer_ingests_skill_invoked_fact() {
|
||||
"skill_id": expected_skill_id,
|
||||
"skill_name": "doc",
|
||||
"event_params": {
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"product_client_id": "codex_web",
|
||||
"skill_scope": "user",
|
||||
"plugin_id": null,
|
||||
"repo_url": null,
|
||||
@@ -3030,6 +3046,7 @@ async fn reducer_includes_plugin_id_for_plugin_skill_invocations() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
let skill_path =
|
||||
PathBuf::from("/Users/abc/.codex/plugins/cache/test/sample/skills/doc/SKILL.md");
|
||||
@@ -3069,6 +3086,7 @@ async fn reducer_ingests_hook_run_fact() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
},
|
||||
hook: HookRunFact {
|
||||
event_name: HookEventName::PostToolUse,
|
||||
@@ -3096,6 +3114,7 @@ async fn reducer_ingests_app_and_plugin_facts() {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
product_client_id: default_product_client_id(),
|
||||
};
|
||||
|
||||
reducer
|
||||
@@ -3152,6 +3171,7 @@ async fn reducer_ingests_plugin_state_changed_fact() {
|
||||
PluginStateChangedInput {
|
||||
plugin: sample_plugin_metadata(),
|
||||
state: PluginState::Disabled,
|
||||
product_client_id: default_product_client_id(),
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
|
||||
@@ -258,37 +258,73 @@ impl AnalyticsEventsClient {
|
||||
}
|
||||
|
||||
pub fn track_plugin_installed(&self, plugin: PluginTelemetryMetadata) {
|
||||
self.track_plugin_installed_with_originator(plugin, &Originator::process_default());
|
||||
}
|
||||
|
||||
pub fn track_plugin_installed_with_originator(
|
||||
&self,
|
||||
plugin: PluginTelemetryMetadata,
|
||||
originator: &Originator,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
|
||||
plugin,
|
||||
state: PluginState::Installed,
|
||||
product_client_id: originator.value().to_string(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_plugin_uninstalled(&self, plugin: PluginTelemetryMetadata) {
|
||||
self.track_plugin_uninstalled_with_originator(plugin, &Originator::process_default());
|
||||
}
|
||||
|
||||
pub fn track_plugin_uninstalled_with_originator(
|
||||
&self,
|
||||
plugin: PluginTelemetryMetadata,
|
||||
originator: &Originator,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
|
||||
plugin,
|
||||
state: PluginState::Uninstalled,
|
||||
product_client_id: originator.value().to_string(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_plugin_enabled(&self, plugin: PluginTelemetryMetadata) {
|
||||
self.track_plugin_enabled_with_originator(plugin, &Originator::process_default());
|
||||
}
|
||||
|
||||
pub fn track_plugin_enabled_with_originator(
|
||||
&self,
|
||||
plugin: PluginTelemetryMetadata,
|
||||
originator: &Originator,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
|
||||
plugin,
|
||||
state: PluginState::Enabled,
|
||||
product_client_id: originator.value().to_string(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_plugin_disabled(&self, plugin: PluginTelemetryMetadata) {
|
||||
self.track_plugin_disabled_with_originator(plugin, &Originator::process_default());
|
||||
}
|
||||
|
||||
pub fn track_plugin_disabled_with_originator(
|
||||
&self,
|
||||
plugin: PluginTelemetryMetadata,
|
||||
originator: &Originator,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
|
||||
plugin,
|
||||
state: PluginState::Disabled,
|
||||
product_client_id: originator.value().to_string(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ use crate::facts::TurnSubmissionType;
|
||||
use crate::now_unix_millis;
|
||||
use codex_app_server_protocol::CodexErrorInfo;
|
||||
use codex_app_server_protocol::CommandExecutionSource;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
@@ -890,13 +889,16 @@ pub(crate) fn codex_app_metadata(
|
||||
thread_id: Some(tracking.thread_id.clone()),
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
app_name: app.app_name,
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
product_client_id: Some(tracking.product_client_id.clone()),
|
||||
invoke_type: app.invocation_type,
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPluginMetadata {
|
||||
pub(crate) fn codex_plugin_metadata(
|
||||
plugin: PluginTelemetryMetadata,
|
||||
product_client_id: String,
|
||||
) -> CodexPluginMetadata {
|
||||
let PluginTelemetryMetadata {
|
||||
plugin_id,
|
||||
remote_plugin_id,
|
||||
@@ -920,7 +922,7 @@ pub(crate) fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPlu
|
||||
.map(|connector_id| connector_id.0)
|
||||
.collect()
|
||||
}),
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
product_client_id: Some(product_client_id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -960,7 +962,7 @@ pub(crate) fn codex_plugin_used_metadata(
|
||||
plugin: PluginTelemetryMetadata,
|
||||
) -> CodexPluginUsedMetadata {
|
||||
CodexPluginUsedMetadata {
|
||||
plugin: codex_plugin_metadata(plugin),
|
||||
plugin: codex_plugin_metadata(plugin, tracking.product_client_id.clone()),
|
||||
thread_id: Some(tracking.thread_id.clone()),
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
|
||||
@@ -40,17 +40,20 @@ pub struct TrackEventsContext {
|
||||
pub model_slug: String,
|
||||
pub thread_id: String,
|
||||
pub turn_id: String,
|
||||
pub product_client_id: String,
|
||||
}
|
||||
|
||||
pub fn build_track_events_context(
|
||||
model_slug: String,
|
||||
thread_id: String,
|
||||
turn_id: String,
|
||||
product_client_id: String,
|
||||
) -> TrackEventsContext {
|
||||
TrackEventsContext {
|
||||
model_slug,
|
||||
thread_id,
|
||||
turn_id,
|
||||
product_client_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +372,7 @@ pub(crate) struct PluginUsedInput {
|
||||
pub(crate) struct PluginStateChangedInput {
|
||||
pub plugin: PluginTelemetryMetadata,
|
||||
pub state: PluginState,
|
||||
pub product_client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
||||
@@ -111,7 +111,6 @@ use codex_app_server_protocol::UserInput;
|
||||
use codex_app_server_protocol::WebSearchAction;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -680,7 +679,7 @@ impl AnalyticsReducer {
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
invoke_type: Some(invocation.invocation_type),
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
product_client_id: Some(tracking.product_client_id.clone()),
|
||||
repo_url,
|
||||
skill_scope: Some(skill_scope.to_string()),
|
||||
plugin_id: invocation.plugin_id,
|
||||
@@ -731,10 +730,14 @@ impl AnalyticsReducer {
|
||||
input: PluginStateChangedInput,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
let PluginStateChangedInput { plugin, state } = input;
|
||||
let PluginStateChangedInput {
|
||||
plugin,
|
||||
state,
|
||||
product_client_id,
|
||||
} = input;
|
||||
let event = CodexPluginEventRequest {
|
||||
event_type: plugin_state_event_type(state),
|
||||
event_params: codex_plugin_metadata(plugin),
|
||||
event_params: codex_plugin_metadata(plugin, product_client_id),
|
||||
};
|
||||
out.push(match state {
|
||||
PluginState::Installed => TrackEventRequest::PluginInstalled(event),
|
||||
|
||||
@@ -887,12 +887,16 @@ impl MessageProcessor {
|
||||
.import(request_id.clone(), params, &request_context)
|
||||
.await
|
||||
.map(|()| None),
|
||||
ClientRequest::ConfigValueWrite { params, .. } => {
|
||||
self.config_processor.value_write(params).await.map(Some)
|
||||
}
|
||||
ClientRequest::ConfigBatchWrite { params, .. } => {
|
||||
self.config_processor.batch_write(params).await.map(Some)
|
||||
}
|
||||
ClientRequest::ConfigValueWrite { params, .. } => self
|
||||
.config_processor
|
||||
.value_write(params, request_context.originator())
|
||||
.await
|
||||
.map(Some),
|
||||
ClientRequest::ConfigBatchWrite { params, .. } => self
|
||||
.config_processor
|
||||
.batch_write(params, request_context.originator())
|
||||
.await
|
||||
.map(Some),
|
||||
ClientRequest::ExperimentalFeatureEnablementSet { params, .. } => {
|
||||
self.config_processor
|
||||
.experimental_feature_enablement_set(request_id.clone(), params)
|
||||
@@ -1127,10 +1131,14 @@ impl MessageProcessor {
|
||||
self.catalog_processor.skills_config_write(params).await
|
||||
}
|
||||
ClientRequest::PluginInstall { params, .. } => {
|
||||
self.plugin_processor.plugin_install(params).await
|
||||
self.plugin_processor
|
||||
.plugin_install(params, request_context.originator())
|
||||
.await
|
||||
}
|
||||
ClientRequest::PluginUninstall { params, .. } => {
|
||||
self.plugin_processor.plugin_uninstall(params).await
|
||||
self.plugin_processor
|
||||
.plugin_uninstall(params, request_context.originator())
|
||||
.await
|
||||
}
|
||||
ClientRequest::ModelList { params, .. } => {
|
||||
self.catalog_processor.model_list(params).await
|
||||
|
||||
@@ -41,6 +41,7 @@ use codex_core::ThreadManager;
|
||||
use codex_features::canonical_feature_for_key;
|
||||
use codex_features::feature_for_key;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
@@ -128,8 +129,9 @@ impl ConfigRequestProcessor {
|
||||
pub(crate) async fn value_write(
|
||||
&self,
|
||||
params: ConfigValueWriteParams,
|
||||
originator: &Originator,
|
||||
) -> Result<ClientResponsePayload, JSONRPCErrorError> {
|
||||
self.handle_config_mutation_result(self.write_value(params).await)
|
||||
self.handle_config_mutation_result(self.write_value(params, originator).await)
|
||||
.await
|
||||
.map(ClientResponsePayload::ConfigValueWrite)
|
||||
}
|
||||
@@ -137,8 +139,9 @@ impl ConfigRequestProcessor {
|
||||
pub(crate) async fn batch_write(
|
||||
&self,
|
||||
params: ConfigBatchWriteParams,
|
||||
originator: &Originator,
|
||||
) -> Result<ClientResponsePayload, JSONRPCErrorError> {
|
||||
self.handle_config_mutation_result(self.batch_write_inner(params).await)
|
||||
self.handle_config_mutation_result(self.batch_write_inner(params, originator).await)
|
||||
.await
|
||||
.map(ClientResponsePayload::ConfigBatchWrite)
|
||||
}
|
||||
@@ -274,6 +277,7 @@ impl ConfigRequestProcessor {
|
||||
async fn write_value(
|
||||
&self,
|
||||
params: ConfigValueWriteParams,
|
||||
originator: &Originator,
|
||||
) -> Result<ConfigWriteResponse, JSONRPCErrorError> {
|
||||
let pending_changes = codex_core_plugins::toggles::collect_plugin_enabled_candidates(
|
||||
[(¶ms.key_path, ¶ms.value)].into_iter(),
|
||||
@@ -283,13 +287,15 @@ impl ConfigRequestProcessor {
|
||||
.write_value(params)
|
||||
.await
|
||||
.map_err(map_error)?;
|
||||
self.emit_plugin_toggle_events(pending_changes).await;
|
||||
self.emit_plugin_toggle_events(pending_changes, originator)
|
||||
.await;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn batch_write_inner(
|
||||
&self,
|
||||
params: ConfigBatchWriteParams,
|
||||
originator: &Originator,
|
||||
) -> Result<ConfigWriteResponse, JSONRPCErrorError> {
|
||||
let reload_user_config = params.reload_user_config;
|
||||
let pending_changes = codex_core_plugins::toggles::collect_plugin_enabled_candidates(
|
||||
@@ -303,7 +309,8 @@ impl ConfigRequestProcessor {
|
||||
.batch_write(params)
|
||||
.await
|
||||
.map_err(map_error)?;
|
||||
self.emit_plugin_toggle_events(pending_changes).await;
|
||||
self.emit_plugin_toggle_events(pending_changes, originator)
|
||||
.await;
|
||||
if reload_user_config {
|
||||
self.reload_user_config().await;
|
||||
}
|
||||
@@ -379,6 +386,7 @@ impl ConfigRequestProcessor {
|
||||
async fn emit_plugin_toggle_events(
|
||||
&self,
|
||||
pending_changes: std::collections::BTreeMap<String, bool>,
|
||||
originator: &Originator,
|
||||
) {
|
||||
for (plugin_id, enabled) in pending_changes {
|
||||
let Ok(plugin_id) = PluginId::parse(&plugin_id) else {
|
||||
@@ -390,9 +398,11 @@ impl ConfigRequestProcessor {
|
||||
)
|
||||
.await;
|
||||
if enabled {
|
||||
self.analytics_events_client.track_plugin_enabled(metadata);
|
||||
self.analytics_events_client
|
||||
.track_plugin_enabled_with_originator(metadata, originator);
|
||||
} else {
|
||||
self.analytics_events_client.track_plugin_disabled(metadata);
|
||||
self.analytics_events_client
|
||||
.track_plugin_disabled_with_originator(metadata, originator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_config::types::McpServerConfig;
|
||||
use codex_core_plugins::remote::RemotePluginScope;
|
||||
use codex_core_plugins::remote::is_valid_remote_plugin_id;
|
||||
use codex_core_plugins::remote::validate_remote_plugin_id;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_mcp::McpOAuthLoginSupport;
|
||||
use codex_mcp::oauth_login_support;
|
||||
use codex_mcp::should_retry_without_scopes;
|
||||
@@ -377,8 +378,9 @@ impl PluginRequestProcessor {
|
||||
pub(crate) async fn plugin_install(
|
||||
&self,
|
||||
params: PluginInstallParams,
|
||||
originator: &Originator,
|
||||
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
|
||||
self.plugin_install_response(params)
|
||||
self.plugin_install_response(params, originator)
|
||||
.await
|
||||
.map(|response| Some(response.into()))
|
||||
}
|
||||
@@ -386,8 +388,9 @@ impl PluginRequestProcessor {
|
||||
pub(crate) async fn plugin_uninstall(
|
||||
&self,
|
||||
params: PluginUninstallParams,
|
||||
originator: &Originator,
|
||||
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
|
||||
self.plugin_uninstall_response(params)
|
||||
self.plugin_uninstall_response(params, originator)
|
||||
.await
|
||||
.map(|response| Some(response.into()))
|
||||
}
|
||||
@@ -1274,6 +1277,7 @@ impl PluginRequestProcessor {
|
||||
async fn plugin_install_response(
|
||||
&self,
|
||||
params: PluginInstallParams,
|
||||
originator: &Originator,
|
||||
) -> Result<PluginInstallResponse, JSONRPCErrorError> {
|
||||
let PluginInstallParams {
|
||||
marketplace_path,
|
||||
@@ -1284,7 +1288,11 @@ impl PluginRequestProcessor {
|
||||
(Some(marketplace_path), None) => marketplace_path,
|
||||
(None, Some(remote_marketplace_name)) => {
|
||||
return self
|
||||
.remote_plugin_install_response(remote_marketplace_name, plugin_name)
|
||||
.remote_plugin_install_response(
|
||||
remote_marketplace_name,
|
||||
plugin_name,
|
||||
originator,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
@@ -1313,7 +1321,7 @@ impl PluginRequestProcessor {
|
||||
};
|
||||
|
||||
let result = plugins_manager
|
||||
.install_plugin(request)
|
||||
.install_plugin_with_originator(request, originator)
|
||||
.await
|
||||
.map_err(Self::plugin_install_error)?;
|
||||
let config = match self.load_latest_config(config_cwd).await {
|
||||
@@ -1355,6 +1363,7 @@ impl PluginRequestProcessor {
|
||||
&self,
|
||||
remote_marketplace_name: String,
|
||||
remote_plugin_id: String,
|
||||
originator: &Originator,
|
||||
) -> Result<PluginInstallResponse, JSONRPCErrorError> {
|
||||
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
|
||||
if !config.features.enabled(Feature::Plugins) {
|
||||
@@ -1441,7 +1450,7 @@ impl PluginRequestProcessor {
|
||||
plugin_telemetry_metadata_from_root(&result.plugin_id, &result.installed_path).await;
|
||||
plugin_metadata.remote_plugin_id = Some(remote_plugin_id);
|
||||
self.analytics_events_client
|
||||
.track_plugin_installed(plugin_metadata);
|
||||
.track_plugin_installed_with_originator(plugin_metadata, originator);
|
||||
|
||||
let plugin_mcp_servers = load_plugin_mcp_servers(result.installed_path.as_path()).await;
|
||||
if !plugin_mcp_servers.is_empty() {
|
||||
@@ -1613,6 +1622,7 @@ impl PluginRequestProcessor {
|
||||
async fn plugin_uninstall_response(
|
||||
&self,
|
||||
params: PluginUninstallParams,
|
||||
originator: &Originator,
|
||||
) -> Result<PluginUninstallResponse, JSONRPCErrorError> {
|
||||
let PluginUninstallParams { plugin_id } = params;
|
||||
if codex_plugin::PluginId::parse(&plugin_id).is_err()
|
||||
@@ -1626,7 +1636,7 @@ impl PluginRequestProcessor {
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
|
||||
plugins_manager
|
||||
.uninstall_plugin(plugin_id)
|
||||
.uninstall_plugin_with_originator(plugin_id, originator)
|
||||
.await
|
||||
.map_err(Self::plugin_uninstall_error)?;
|
||||
match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
|
||||
@@ -59,6 +59,7 @@ use codex_core_skills::SkillMetadata;
|
||||
use codex_hooks::plugin_hook_declarations;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_plugin::AppConnectorId;
|
||||
use codex_plugin::PluginCapabilitySummary;
|
||||
use codex_plugin::PluginId;
|
||||
@@ -831,13 +832,22 @@ impl PluginsManager {
|
||||
pub async fn install_plugin(
|
||||
&self,
|
||||
request: PluginInstallRequest,
|
||||
) -> Result<PluginInstallOutcome, PluginInstallError> {
|
||||
self.install_plugin_with_originator(request, &Originator::process_default())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn install_plugin_with_originator(
|
||||
&self,
|
||||
request: PluginInstallRequest,
|
||||
originator: &Originator,
|
||||
) -> Result<PluginInstallOutcome, PluginInstallError> {
|
||||
let resolved = find_installable_marketplace_plugin(
|
||||
&request.marketplace_path,
|
||||
&request.plugin_name,
|
||||
self.restriction_product,
|
||||
)?;
|
||||
self.install_resolved_plugin(resolved).await
|
||||
self.install_resolved_plugin(resolved, originator).await
|
||||
}
|
||||
|
||||
pub async fn install_plugin_with_remote_sync(
|
||||
@@ -860,12 +870,14 @@ impl PluginsManager {
|
||||
)
|
||||
.await
|
||||
.map_err(PluginInstallError::from)?;
|
||||
self.install_resolved_plugin(resolved).await
|
||||
self.install_resolved_plugin(resolved, &Originator::process_default())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn install_resolved_plugin(
|
||||
&self,
|
||||
resolved: ResolvedMarketplacePlugin,
|
||||
originator: &Originator,
|
||||
) -> Result<PluginInstallOutcome, PluginInstallError> {
|
||||
let auth_policy = resolved.policy.authentication;
|
||||
let plugin_version =
|
||||
@@ -909,9 +921,10 @@ impl PluginsManager {
|
||||
Err(err) => err.into_inner().clone(),
|
||||
};
|
||||
if let Some(analytics_events_client) = analytics_events_client {
|
||||
analytics_events_client.track_plugin_installed(
|
||||
analytics_events_client.track_plugin_installed_with_originator(
|
||||
plugin_telemetry_metadata_from_root(&result.plugin_id, &result.installed_path)
|
||||
.await,
|
||||
originator,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -924,8 +937,17 @@ impl PluginsManager {
|
||||
}
|
||||
|
||||
pub async fn uninstall_plugin(&self, plugin_id: String) -> Result<(), PluginUninstallError> {
|
||||
self.uninstall_plugin_with_originator(plugin_id, &Originator::process_default())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn uninstall_plugin_with_originator(
|
||||
&self,
|
||||
plugin_id: String,
|
||||
originator: &Originator,
|
||||
) -> Result<(), PluginUninstallError> {
|
||||
let plugin_id = PluginId::parse(&plugin_id)?;
|
||||
self.uninstall_plugin_id(plugin_id).await
|
||||
self.uninstall_plugin_id(plugin_id, originator).await
|
||||
}
|
||||
|
||||
pub async fn uninstall_plugin_with_remote_sync(
|
||||
@@ -946,10 +968,15 @@ impl PluginsManager {
|
||||
)
|
||||
.await
|
||||
.map_err(PluginUninstallError::from)?;
|
||||
self.uninstall_plugin_id(plugin_id).await
|
||||
self.uninstall_plugin_id(plugin_id, &Originator::process_default())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn uninstall_plugin_id(&self, plugin_id: PluginId) -> Result<(), PluginUninstallError> {
|
||||
async fn uninstall_plugin_id(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
originator: &Originator,
|
||||
) -> Result<(), PluginUninstallError> {
|
||||
let plugin_telemetry = if self.store.active_plugin_root(&plugin_id).is_some() {
|
||||
Some(installed_plugin_telemetry_metadata(self.codex_home.as_path(), &plugin_id).await)
|
||||
} else {
|
||||
@@ -972,7 +999,8 @@ impl PluginsManager {
|
||||
if let Some(plugin_telemetry) = plugin_telemetry
|
||||
&& let Some(analytics_events_client) = analytics_events_client
|
||||
{
|
||||
analytics_events_client.track_plugin_uninstalled(plugin_telemetry);
|
||||
analytics_events_client
|
||||
.track_plugin_uninstalled_with_originator(plugin_telemetry, originator);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -589,6 +589,7 @@ fn hook_run_analytics_payload(
|
||||
.turn_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| turn_context.sub_id.clone()),
|
||||
turn_context.originator.value().to_string(),
|
||||
),
|
||||
HookRunFact {
|
||||
event_name: completed.run.event_name,
|
||||
|
||||
@@ -948,6 +948,7 @@ async fn maybe_track_codex_app_used(
|
||||
turn_context.model_info.slug.clone(),
|
||||
sess.conversation_id.to_string(),
|
||||
turn_context.sub_id.clone(),
|
||||
turn_context.originator.value().to_string(),
|
||||
);
|
||||
sess.services.analytics_events_client.track_app_used(
|
||||
tracking,
|
||||
|
||||
@@ -464,6 +464,7 @@ async fn build_skills_and_plugins(
|
||||
turn_context.model_info.slug.clone(),
|
||||
sess.conversation_id.to_string(),
|
||||
turn_context.sub_id.clone(),
|
||||
turn_context.originator.value().to_string(),
|
||||
);
|
||||
let loaded_plugins = sess
|
||||
.services
|
||||
|
||||
@@ -102,6 +102,7 @@ pub(crate) async fn maybe_emit_implicit_skill_invocation(
|
||||
turn_context.model_info.slug.clone(),
|
||||
sess.conversation_id.to_string(),
|
||||
turn_context.sub_id.clone(),
|
||||
turn_context.originator.value().to_string(),
|
||||
),
|
||||
vec![invocation],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user