Expose connector candidates in external agent detection (#36218)

## What changed

- Add a `connectors` array to `ExternalAgentConfigDetectResponse` with each candidate's normalized name, detected session count, and detection source.
- Define detection sources for remote MCP server configuration and session tool use, and export the corresponding JSON Schema and TypeScript types.
- Default `connectors` to an empty array when deserializing responses from older servers.

## Testing

- Add a protocol test covering deserialization when `connectors` is omitted.

GitOrigin-RevId: b65d76791099385226ed54411f4c1ef902be43d8
This commit is contained in:
charlesgong-openai authored and copyberry committed 2026-07-30 20:58:06 +00:00
1 parent 97576b1794
commit e6cfd40c3f
11 files changed
+157 -2

No files matched your search

@@ -9763,6 +9763,13 @@
"ExternalAgentConfigDetectResponse": { "ExternalAgentConfigDetectResponse": {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"properties": { "properties": {
"connectors": {
"default": [],
"items": {
"$ref": "#/definitions/v2/ExternalAgentDetectedConnectorCandidate"
},
"type": "array"
},
"items": { "items": {
"items": { "items": {
"$ref": "#/definitions/v2/ExternalAgentConfigMigrationItem" "$ref": "#/definitions/v2/ExternalAgentConfigMigrationItem"
@@ -10174,6 +10181,34 @@
], ],
"type": "string" "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": { "ExternalAgentImportedConnectorCandidate": {
"properties": { "properties": {
"name": { "name": {
@@ -5941,6 +5941,13 @@
"ExternalAgentConfigDetectResponse": { "ExternalAgentConfigDetectResponse": {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"properties": { "properties": {
"connectors": {
"default": [],
"items": {
"$ref": "#/definitions/ExternalAgentDetectedConnectorCandidate"
},
"type": "array"
},
"items": { "items": {
"items": { "items": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItem" "$ref": "#/definitions/ExternalAgentConfigMigrationItem"
@@ -6352,6 +6359,34 @@
], ],
"type": "string" "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": { "ExternalAgentImportedConnectorCandidate": {
"properties": { "properties": {
"name": { "name": {
@@ -59,6 +59,34 @@
], ],
"type": "string" "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": { "HookMigration": {
"properties": { "properties": {
"name": { "name": {
@@ -204,6 +232,13 @@
} }
}, },
"properties": { "properties": {
"connectors": {
"default": [],
"items": {
"$ref": "#/definitions/ExternalAgentDetectedConnectorCandidate"
},
"type": "array"
},
"items": { "items": {
"items": { "items": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItem" "$ref": "#/definitions/ExternalAgentConfigMigrationItem"
@@ -2,5 +2,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // 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 { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem";
import type { ExternalAgentDetectedConnectorCandidate } from "./ExternalAgentDetectedConnectorCandidate";
export type ExternalAgentConfigDetectResponse = { items: Array<ExternalAgentConfigMigrationItem>, }; export type ExternalAgentConfigDetectResponse = { items: Array<ExternalAgentConfigMigrationItem>, connectors: Array<ExternalAgentDetectedConnectorCandidate>, };
@@ -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, };
@@ -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";
@@ -132,6 +132,8 @@ export type { ExternalAgentConfigImportResponse } from "./ExternalAgentConfigImp
export type { ExternalAgentConfigImportTypeResult } from "./ExternalAgentConfigImportTypeResult"; export type { ExternalAgentConfigImportTypeResult } from "./ExternalAgentConfigImportTypeResult";
export type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; export type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem";
export type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; export type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType";
export type { ExternalAgentDetectedConnectorCandidate } from "./ExternalAgentDetectedConnectorCandidate";
export type { ExternalAgentDetectedConnectorSource } from "./ExternalAgentDetectedConnectorSource";
export type { ExternalAgentImportedConnectorCandidate } from "./ExternalAgentImportedConnectorCandidate"; export type { ExternalAgentImportedConnectorCandidate } from "./ExternalAgentImportedConnectorCandidate";
export type { ExternalAgentImportedConnectorSource } from "./ExternalAgentImportedConnectorSource"; export type { ExternalAgentImportedConnectorSource } from "./ExternalAgentImportedConnectorSource";
export type { FeedbackRequirements } from "./FeedbackRequirements"; export type { FeedbackRequirements } from "./FeedbackRequirements";
@@ -714,6 +714,8 @@ pub struct ExternalAgentConfigMigrationItem {
#[ts(export_to = "v2/")] #[ts(export_to = "v2/")]
pub struct ExternalAgentConfigDetectResponse { pub struct ExternalAgentConfigDetectResponse {
pub items: Vec<ExternalAgentConfigMigrationItem>, pub items: Vec<ExternalAgentConfigMigrationItem>,
#[serde(default)]
pub connectors: Vec<ExternalAgentDetectedConnectorCandidate>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
@@ -876,6 +878,23 @@ pub struct ExternalAgentImportedConnectorCandidate {
pub source: ExternalAgentImportedConnectorSource, 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)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")] #[ts(export_to = "v2/")]
@@ -72,6 +72,22 @@ fn test_absolute_path() -> AbsolutePathBuf {
absolute_path("readable") absolute_path("readable")
} }
#[test]
fn external_agent_config_detect_response_defaults_connectors_for_older_servers() {
let response = serde_json::from_value::<ExternalAgentConfigDetectResponse>(json!({
"items": [],
}))
.expect("older detect response should deserialize");
assert_eq!(
response,
ExternalAgentConfigDetectResponse {
items: Vec::new(),
connectors: Vec::new(),
}
);
}
#[test] #[test]
fn thread_background_terminals_list_response_round_trips_foreign_paths() { fn thread_background_terminals_list_response_round_trips_foreign_paths() {
for (uri, expected_cwd) in [ for (uri, expected_cwd) in [
+1 -1
View File
@@ -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`. - `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. - `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`. - `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` — 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. - `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`. - `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`.
@@ -30,6 +30,7 @@ use codex_state::ExternalAgentConfigImportSuccessRecord;
pub(super) fn detect_response(items: Vec<CoreMigrationItem>) -> ExternalAgentConfigDetectResponse { pub(super) fn detect_response(items: Vec<CoreMigrationItem>) -> ExternalAgentConfigDetectResponse {
ExternalAgentConfigDetectResponse { ExternalAgentConfigDetectResponse {
items: items.into_iter().map(protocol_migration_item).collect(), items: items.into_iter().map(protocol_migration_item).collect(),
connectors: Vec::new(),
} }
} }