diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 512c7346ef..10aea90a00 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -9763,6 +9763,13 @@ "ExternalAgentConfigDetectResponse": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { + "connectors": { + "default": [], + "items": { + "$ref": "#/definitions/v2/ExternalAgentDetectedConnectorCandidate" + }, + "type": "array" + }, "items": { "items": { "$ref": "#/definitions/v2/ExternalAgentConfigMigrationItem" @@ -10174,6 +10181,34 @@ ], "type": "string" }, + "ExternalAgentDetectedConnectorCandidate": { + "properties": { + "name": { + "type": "string" + }, + "sessionCount": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "source": { + "$ref": "#/definitions/v2/ExternalAgentDetectedConnectorSource" + } + }, + "required": [ + "name", + "sessionCount", + "source" + ], + "type": "object" + }, + "ExternalAgentDetectedConnectorSource": { + "enum": [ + "remoteMcpServersConfig", + "sessionToolUse" + ], + "type": "string" + }, "ExternalAgentImportedConnectorCandidate": { "properties": { "name": { diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index af02ed4da9..fb3811047e 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -5941,6 +5941,13 @@ "ExternalAgentConfigDetectResponse": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { + "connectors": { + "default": [], + "items": { + "$ref": "#/definitions/ExternalAgentDetectedConnectorCandidate" + }, + "type": "array" + }, "items": { "items": { "$ref": "#/definitions/ExternalAgentConfigMigrationItem" @@ -6352,6 +6359,34 @@ ], "type": "string" }, + "ExternalAgentDetectedConnectorCandidate": { + "properties": { + "name": { + "type": "string" + }, + "sessionCount": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "source": { + "$ref": "#/definitions/ExternalAgentDetectedConnectorSource" + } + }, + "required": [ + "name", + "sessionCount", + "source" + ], + "type": "object" + }, + "ExternalAgentDetectedConnectorSource": { + "enum": [ + "remoteMcpServersConfig", + "sessionToolUse" + ], + "type": "string" + }, "ExternalAgentImportedConnectorCandidate": { "properties": { "name": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigDetectResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigDetectResponse.json index 2a75dd361f..b328531e97 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigDetectResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ExternalAgentConfigDetectResponse.json @@ -59,6 +59,34 @@ ], "type": "string" }, + "ExternalAgentDetectedConnectorCandidate": { + "properties": { + "name": { + "type": "string" + }, + "sessionCount": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "source": { + "$ref": "#/definitions/ExternalAgentDetectedConnectorSource" + } + }, + "required": [ + "name", + "sessionCount", + "source" + ], + "type": "object" + }, + "ExternalAgentDetectedConnectorSource": { + "enum": [ + "remoteMcpServersConfig", + "sessionToolUse" + ], + "type": "string" + }, "HookMigration": { "properties": { "name": { @@ -204,6 +232,13 @@ } }, "properties": { + "connectors": { + "default": [], + "items": { + "$ref": "#/definitions/ExternalAgentDetectedConnectorCandidate" + }, + "type": "array" + }, "items": { "items": { "$ref": "#/definitions/ExternalAgentConfigMigrationItem" diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigDetectResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigDetectResponse.ts index f220e3b616..5df7e69cf5 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigDetectResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentConfigDetectResponse.ts @@ -2,5 +2,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; +import type { ExternalAgentDetectedConnectorCandidate } from "./ExternalAgentDetectedConnectorCandidate"; -export type ExternalAgentConfigDetectResponse = { items: Array, }; +export type ExternalAgentConfigDetectResponse = { items: Array, connectors: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorCandidate.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorCandidate.ts new file mode 100644 index 0000000000..d1932d7dcf --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorCandidate.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExternalAgentDetectedConnectorSource } from "./ExternalAgentDetectedConnectorSource"; + +export type ExternalAgentDetectedConnectorCandidate = { name: string, sessionCount: number, source: ExternalAgentDetectedConnectorSource, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorSource.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorSource.ts new file mode 100644 index 0000000000..c4c608c47d --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExternalAgentDetectedConnectorSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExternalAgentDetectedConnectorSource = "remoteMcpServersConfig" | "sessionToolUse"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 7a06b52489..9dabc65141 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -132,6 +132,8 @@ export type { ExternalAgentConfigImportResponse } from "./ExternalAgentConfigImp export type { ExternalAgentConfigImportTypeResult } from "./ExternalAgentConfigImportTypeResult"; export type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; export type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; +export type { ExternalAgentDetectedConnectorCandidate } from "./ExternalAgentDetectedConnectorCandidate"; +export type { ExternalAgentDetectedConnectorSource } from "./ExternalAgentDetectedConnectorSource"; export type { ExternalAgentImportedConnectorCandidate } from "./ExternalAgentImportedConnectorCandidate"; export type { ExternalAgentImportedConnectorSource } from "./ExternalAgentImportedConnectorSource"; export type { FeedbackRequirements } from "./FeedbackRequirements"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2/config.rs b/codex-rs/app-server-protocol/src/protocol/v2/config.rs index 82b4ca9fba..5dcd22cc52 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/config.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/config.rs @@ -714,6 +714,8 @@ pub struct ExternalAgentConfigMigrationItem { #[ts(export_to = "v2/")] pub struct ExternalAgentConfigDetectResponse { pub items: Vec, + #[serde(default)] + pub connectors: Vec, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] @@ -876,6 +878,23 @@ pub struct ExternalAgentImportedConnectorCandidate { pub source: ExternalAgentImportedConnectorSource, } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub enum ExternalAgentDetectedConnectorSource { + RemoteMcpServersConfig, + SessionToolUse, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ExternalAgentDetectedConnectorCandidate { + pub name: String, + pub session_count: u32, + pub source: ExternalAgentDetectedConnectorSource, +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] diff --git a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs index 4558a59992..6027f470f1 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -72,6 +72,22 @@ fn test_absolute_path() -> AbsolutePathBuf { absolute_path("readable") } +#[test] +fn external_agent_config_detect_response_defaults_connectors_for_older_servers() { + let response = serde_json::from_value::(json!({ + "items": [], + })) + .expect("older detect response should deserialize"); + + assert_eq!( + response, + ExternalAgentConfigDetectResponse { + items: Vec::new(), + connectors: Vec::new(), + } + ); +} + #[test] fn thread_background_terminals_list_response_round_trips_foreign_paths() { for (uri, expected_cwd) in [ diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 0ff7854162..cf6d6fdf13 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -247,7 +247,7 @@ Example with notification opt-out: - `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); accepts an optional absolute `cwd` to target setup for a specific workspace, returns `{ started: true }` immediately, and later emits `windowsSandbox/setupCompleted`. - `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id. - `config/read` — fetch the runtime-effective config after resolving config layering and managed requirements, including opaque `desktop` values stored in `config.toml`. -- `externalAgentConfig/detect` — detect migratable external-agent artifacts with `includeHome`, optional `cwds`, and an optional `migrationSource` selector. Omitted, `null`, or unrecognized migration-source values retain the default behavior. The deprecated optional `source` field remains accepted for compatibility but does not select the migration source. Each detected item includes `cwd` (`null` for home), and multi-item migrations may additionally include structured `details` with plugin ids, skill names, memory, session metadata, or other artifact names. +- `externalAgentConfig/detect` — detect migratable external-agent artifacts with `includeHome`, optional `cwds`, and an optional `migrationSource` selector. Omitted, `null`, or unrecognized migration-source values retain the default behavior. The deprecated optional `source` field remains accepted for compatibility but does not select the migration source. Each detected item includes `cwd` (`null` for home), and multi-item migrations may additionally include structured `details` with plugin ids, skill names, memory, session metadata, or other artifact names. The response also includes connector candidates inferred from detected source sessions, with a normalized display `name`, the number of detected sessions that used the connector, and the source metadata field used for detection. - `externalAgentConfig/import` — apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home) and any `details` returned by detect. Pass the same optional `migrationSource` used for detection so the server reads from the matching source; omitted, `null`, or unrecognized values retain the default behavior. The optional `source` identifies the product that initiated the import, while the optional opaque `providerId` attributes analytics to the provider selected by that product without affecting migration-source selection. The response acknowledges the synchronous import phase with an `importId`. Expected migration failures are reported as per-item failures rather than JSON-RPC errors, so the server still returns that `importId` and emits `externalAgentConfig/import/completed` with the same ID once all synchronous and background work finishes. The completion notification contains type-level `itemTypeResults` with successes and failures, including raw failure messages for the client to report separately. - `externalAgentConfig/import/readHistories` — read completed import histories and connector candidates detected from successfully imported session histories. Successful session entries include the original imported title when one was available. Connector candidates include a normalized display `name`, the number of imported sessions that used the connector, and the source metadata field used for detection. - `config/value/write` — write a single config key/value to the user's config.toml on disk; dotted paths such as `desktop.someKey` use the same generic write surface. Writes that overlap a managed requirement are rejected with `configRequirementReadonly`. diff --git a/codex-rs/app-server/src/external_agent_migration/protocol.rs b/codex-rs/app-server/src/external_agent_migration/protocol.rs index 702554bde3..f4e233a828 100644 --- a/codex-rs/app-server/src/external_agent_migration/protocol.rs +++ b/codex-rs/app-server/src/external_agent_migration/protocol.rs @@ -30,6 +30,7 @@ use codex_state::ExternalAgentConfigImportSuccessRecord; pub(super) fn detect_response(items: Vec) -> ExternalAgentConfigDetectResponse { ExternalAgentConfigDetectResponse { items: items.into_iter().map(protocol_migration_item).collect(), + connectors: Vec::new(), } }