Record externally completed agent config imports (#34981)

## Why

Clients that complete an external agent config import outside the app server need a way to include the result in import history.

## What changed

- Add `externalAgentConfig/import/recordHistory` to persist completed item results and return a generated import ID.
- Store the optional provider ID for regular imports and expose it when reading import history.
- Update the generated JSON and TypeScript protocol schemas for the new request, response, and history field.

## Testing

- Cover recording and reading an externally completed import through the app-server protocol.
- Cover provider ID persistence and updates in the state runtime.

GitOrigin-RevId: 935401e899ec9eeb38f54c182e45a16642411deb
This commit is contained in:
charlesgong-openai authored and copyberry committed 2026-07-23 17:55:22 +00:00
1 parent 8d34c06672
commit ce803c45ae
22 files changed
+632 -22

No files matched your search

@@ -14,10 +14,13 @@ use codex_app_server_protocol::ExternalAgentConfigDetectParams;
use codex_app_server_protocol::ExternalAgentConfigDetectResponse;
use codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification;
use codex_app_server_protocol::ExternalAgentConfigImportHistoriesReadResponse;
use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordParams;
use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordResponse;
use codex_app_server_protocol::ExternalAgentConfigImportItemTypeFailure as ProtocolImportFailure;
use codex_app_server_protocol::ExternalAgentConfigImportParams;
use codex_app_server_protocol::ExternalAgentConfigImportProgressNotification;
use codex_app_server_protocol::ExternalAgentConfigImportResponse;
use codex_app_server_protocol::ExternalAgentConfigImportTypeResult as ProtocolImportTypeResult;
use codex_app_server_protocol::ExternalAgentConfigMigrationItem;
use codex_app_server_protocol::ExternalAgentConfigMigrationItemType;
use codex_app_server_protocol::ExternalAgentImportedConnectorCandidate;
@@ -172,7 +175,7 @@ impl ExternalAgentConfigRequestProcessor {
}
let import_id = Uuid::new_v4().to_string();
let analytics_source = params.source.clone().unwrap_or_default();
let provider_id = params.provider_id.clone().unwrap_or_default();
let provider_id = params.provider_id.clone();
let migration_service = self
.migration_service
.with_migration_source(params.migration_source.as_deref());
@@ -378,6 +381,27 @@ impl ExternalAgentConfigRequestProcessor {
Ok(ExternalAgentConfigImportHistoriesReadResponse { data, connectors })
}
pub(crate) async fn record_import_history(
&self,
params: ExternalAgentConfigImportHistoryRecordParams,
) -> Result<ExternalAgentConfigImportHistoryRecordResponse, JSONRPCErrorError> {
let state_db = self
.state_db
.as_ref()
.ok_or_else(|| internal_error("state database is unavailable"))?;
let import_id = Uuid::new_v4().to_string();
record_import_history(
state_db,
import_id.as_str(),
Some(params.provider_id.as_str()),
&params.item_type_results,
)
.await
.map_err(|err| internal_error(format!("failed to record import history: {err}")))?;
Ok(ExternalAgentConfigImportHistoryRecordResponse { import_id })
}
fn validate_pending_session_imports(
&self,
params: &ExternalAgentConfigImportParams,
@@ -483,7 +507,7 @@ async fn send_completed_import_notification(
analytics_events_client: &AnalyticsEventsClient,
import_id: String,
analytics_source: String,
provider_id: String,
provider_id: Option<String>,
item_results: &[CoreImportItemResult],
) {
let notification = completed_notification(import_id, item_results);
@@ -491,11 +515,13 @@ async fn send_completed_import_notification(
track_completed_import_notification(
analytics_events_client,
&analytics_source,
&provider_id,
provider_id.as_deref().unwrap_or_default(),
&notification,
);
if let Some(state_db) = state_db
&& let Err(err) = record_completed_import_notification(state_db, &notification).await
&& let Err(err) =
record_completed_import_notification(state_db, provider_id.as_deref(), &notification)
.await
{
tracing::warn!(
import_id = %notification.import_id,
@@ -586,10 +612,25 @@ fn analytics_migration_item_type(item_type: ExternalAgentConfigMigrationItemType
async fn record_completed_import_notification(
state_db: &StateDbHandle,
provider_id: Option<&str>,
notification: &ExternalAgentConfigImportCompletedNotification,
) -> anyhow::Result<()> {
let successes = notification
.item_type_results
record_import_history(
state_db,
notification.import_id.as_str(),
provider_id,
&notification.item_type_results,
)
.await
}
async fn record_import_history(
state_db: &StateDbHandle,
import_id: &str,
provider_id: Option<&str>,
item_type_results: &[ProtocolImportTypeResult],
) -> anyhow::Result<()> {
let successes = item_type_results
.iter()
.flat_map(|type_result| type_result.successes.iter())
.map(|success| {
@@ -601,8 +642,7 @@ async fn record_completed_import_notification(
})
})
.collect::<anyhow::Result<Vec<_>>>()?;
let failures = notification
.item_type_results
let failures = item_type_results
.iter()
.flat_map(|type_result| type_result.failures.iter())
.map(|failure| {
@@ -619,7 +659,8 @@ async fn record_completed_import_notification(
.collect::<anyhow::Result<Vec<_>>>()?;
state_db
.record_external_agent_config_import_completed(
notification.import_id.as_str(),
import_id,
provider_id,
&successes,
&failures,
)