Files
codex/codex-rs/ext/extension-api/examples/enabled_extensions.rs
jif-oai 68e045a631 Make context contributors async (#22491)
## Summary
- make ContextContributor return a boxed Send future
- await context contributors during initial context assembly
- update existing contributors and extension-api examples for the async
contract

## Testing
- cargo test -p codex-extension-api --examples
- cargo test -p codex-git-attribution
- cargo test -p codex-core
build_initial_context_includes_git_attribution_from_extensions --
--nocapture
- cargo test -p codex-core
build_initial_context_omits_git_attribution_when_feature_is_disabled --
--nocapture
- cargo test -p codex-core (fails in unrelated
agent::control::tests::spawn_agent_fork_last_n_turns_keeps_only_recent_turns
stack overflow)
- just fix -p codex-extension-api
- just fix -p codex-git-attribution
- just fix -p codex-core
- cargo clippy -p codex-extension-api --examples
2026-05-13 16:43:28 +02:00

94 lines
2.9 KiB
Rust

#[path = "enabled_extensions/shared_state_extension.rs"]
mod shared_state_extension;
use std::future::Future;
use std::pin::pin;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use codex_extension_api::ExtensionData;
use codex_extension_api::ExtensionRegistryBuilder;
use shared_state_extension::recorded_style_contributions;
use shared_state_extension::recorded_usage_contributions;
fn main() {
// 1. Install the contributors for the thread-start input type this host exposes.
let mut builder = ExtensionRegistryBuilder::<()>::new();
shared_state_extension::install(&mut builder);
let registry = builder.build();
// 2. The host decides which stores are shared.
let session_store = ExtensionData::new("session");
let first_thread_store = ExtensionData::new("thread-1");
let second_thread_store = ExtensionData::new("thread-2");
// 3. Reusing the same session store shares session state across threads.
let first_thread_fragments = block_on_ready(contribute_prompt(
&registry,
&session_store,
&first_thread_store,
));
block_on_ready(contribute_prompt(
&registry,
&session_store,
&first_thread_store,
));
block_on_ready(contribute_prompt(
&registry,
&session_store,
&second_thread_store,
));
println!("first prompt fragments: {}", first_thread_fragments.len());
println!(
"session style contributions: {}",
recorded_style_contributions(&session_store)
);
println!(
"session usage contributions: {}",
recorded_usage_contributions(&session_store)
);
println!(
"first thread style contributions: {}",
recorded_style_contributions(&first_thread_store)
);
println!(
"first thread usage contributions: {}",
recorded_usage_contributions(&first_thread_store)
);
println!(
"second thread style contributions: {}",
recorded_style_contributions(&second_thread_store)
);
println!(
"second thread usage contributions: {}",
recorded_usage_contributions(&second_thread_store)
);
}
async fn contribute_prompt(
registry: &codex_extension_api::ExtensionRegistry<()>,
session_store: &ExtensionData,
thread_store: &ExtensionData,
) -> Vec<codex_extension_api::PromptFragment> {
let mut fragments = Vec::new();
for contributor in registry.context_contributors() {
fragments.extend(contributor.contribute(session_store, thread_store).await);
}
fragments
}
fn block_on_ready<F>(future: F) -> F::Output
where
F: Future,
{
let waker = Waker::noop();
let mut context = Context::from_waker(waker);
let mut future = pin!(future);
match future.as_mut().poll(&mut context) {
Poll::Ready(output) => output,
Poll::Pending => panic!("example context contributors should complete immediately"),
}
}