mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
## Why Connector declarations currently enter Codex through broad plugin capability summaries, then MCP setup, turn tooling, and `app/list` each reconstruct the same information. That makes executor-selected connectors difficult to add without coupling connector behavior to the host plugin loader. This PR introduces a small connector-owned value that later stack layers can populate before thread startup. ## What changed - Move the pure app-declaration parser into `codex-connectors`, preserving declaration order and category cleanup while leaving host-side validation and deduplication unchanged. - Add an immutable `ConnectorSnapshot` with ordered connector IDs and plugin display-name provenance. - Adapt the existing local-plugin capability summaries into that snapshot at current consumer boundaries. - Use the snapshot for MCP tool provenance, turn connector inventory, and `app/list`. - Keep the crate API narrow: no test-only snapshot accessors are exposed. The externally visible behavior is unchanged. Connector tools still come from the orchestrator-owned `/ps/mcp` server, and local plugin enablement remains owned by the existing plugin loader. ## Stack scope This is the foundation only. It does not read selected executor packages or change thread startup. #29852 adds the executor-backed declaration reader, and #29856 composes selected declarations into a thread snapshot.
48 lines
1.4 KiB
Rust
48 lines
1.4 KiB
Rust
use codex_plugin::AppConnectorId;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::ConnectorSnapshot;
|
|
use super::PluginConnectorSource;
|
|
|
|
#[test]
|
|
fn snapshot_merges_sources_in_order_and_dedupes_provenance() {
|
|
let host_source = source("host", "Zulu", &["calendar", "calendar"]);
|
|
let host = ConnectorSnapshot::from_plugin_sources([
|
|
source("skills", "Skills only", &[]),
|
|
host_source.clone(),
|
|
]);
|
|
let selected = ConnectorSnapshot::from_plugin_sources([
|
|
source("selected-a", "Alpha", &["drive", "calendar"]),
|
|
source("selected-b", "Alpha", &["calendar"]),
|
|
]);
|
|
|
|
let merged = host.merged_with(&selected);
|
|
|
|
assert_eq!(host.sources, vec![host_source]);
|
|
assert_eq!(
|
|
merged.connector_ids(),
|
|
&[
|
|
AppConnectorId("calendar".to_string()),
|
|
AppConnectorId("drive".to_string()),
|
|
]
|
|
);
|
|
assert_eq!(
|
|
merged.plugin_display_names_for_connector_id("calendar"),
|
|
&["Alpha".to_string(), "Zulu".to_string()]
|
|
);
|
|
assert_eq!(
|
|
merged.plugin_display_names_for_connector_id("missing"),
|
|
&[] as &[String]
|
|
);
|
|
}
|
|
|
|
fn source(id: &str, display_name: &str, connector_ids: &[&str]) -> PluginConnectorSource {
|
|
PluginConnectorSource::from_connector_ids(
|
|
id,
|
|
display_name,
|
|
connector_ids
|
|
.iter()
|
|
.map(|id| AppConnectorId((*id).to_string())),
|
|
)
|
|
}
|