feat: move extension scope ids into ExtensionData (#22490)

## Summary
- add a scoped level_id to ExtensionData and expose it through
level_id()
- remove thread_id/turn_id parameters from extension contributor inputs
where the scoped ExtensionData already carries that identity
- move turn-scoped extension data onto TurnContext so token usage and
lifecycle contributors can share the same turn store

## Testing
- cargo check -p codex-extension-api -p codex-core --tests
- cargo test -p codex-extension-api
- cargo test -p codex-guardian
- cargo test -p codex-core --lib
record_token_usage_info_notifies_extension_contributors
- cargo test -p codex-core --lib
submission_loop_channel_close_emits_thread_stop_lifecycle
- cargo test -p codex-core --lib
submission_loop_channel_close_aborts_active_turn_before_thread_stop_lifecycle
- just fix -p codex-extension-api
- just fix -p codex-guardian
- just fix -p codex-core
- just fmt

## Note
- Attempted cargo test -p codex-core; it aborted in
agent::control::tests::spawn_agent_fork_last_n_turns_keeps_only_recent_turns
with the existing stack overflow before the full suite completed.
This commit is contained in:
jif-oai
2026-05-13 16:13:16 +02:00
committed by GitHub
parent 99157f3797
commit 1dcc89f1d4
16 changed files with 77 additions and 107 deletions

View File

@@ -13,9 +13,9 @@ fn main() {
let registry = builder.build();
// 2. The host decides which stores are shared.
let session_store = ExtensionData::new();
let first_thread_store = ExtensionData::new();
let second_thread_store = ExtensionData::new();
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 = contribute_prompt(&registry, &session_store, &first_thread_store);

View File

@@ -1,7 +1,6 @@
use std::future::Future;
use std::sync::Arc;
use codex_protocol::ThreadId;
use codex_protocol::items::TurnItem;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::TokenUsageInfo;
@@ -79,8 +78,7 @@ pub trait TokenUsageContributor: Send + Sync {
&self,
_session_store: &ExtensionData,
_thread_store: &ExtensionData,
_thread_id: ThreadId,
_turn_id: &str,
_turn_store: &ExtensionData,
_token_usage: &TokenUsageInfo,
) {
}

View File

@@ -1,11 +1,7 @@
use codex_protocol::ThreadId;
use crate::ExtensionData;
/// Input supplied when the host starts a runtime for a thread.
pub struct ThreadStartInput<'a, C> {
/// Identifier for the thread whose runtime is starting.
pub thread_id: ThreadId,
/// Host configuration visible at thread start.
pub config: &'a C,
/// Store scoped to the host session runtime.
@@ -16,8 +12,6 @@ pub struct ThreadStartInput<'a, C> {
/// Input supplied when the host resumes an existing thread.
pub struct ThreadResumeInput<'a> {
/// Identifier for the thread being resumed.
pub thread_id: ThreadId,
/// Store scoped to the host session runtime.
pub session_store: &'a ExtensionData,
/// Store scoped to this thread runtime.
@@ -26,8 +20,6 @@ pub struct ThreadResumeInput<'a> {
/// Input supplied when the host stops a thread runtime.
pub struct ThreadStopInput<'a> {
/// Identifier for the thread whose runtime is stopping.
pub thread_id: ThreadId,
/// Store scoped to the host session runtime.
pub session_store: &'a ExtensionData,
/// Store scoped to this thread runtime.

View File

@@ -1,14 +1,9 @@
use codex_protocol::ThreadId;
use codex_protocol::protocol::TurnAbortReason;
use crate::ExtensionData;
/// Input supplied when the host starts a turn.
pub struct TurnStartInput<'a> {
/// Identifier for the thread containing this turn.
pub thread_id: ThreadId,
/// Identifier for the turn that is starting.
pub turn_id: &'a str,
/// Store scoped to the host session runtime.
pub session_store: &'a ExtensionData,
/// Store scoped to this thread runtime.
@@ -19,10 +14,6 @@ pub struct TurnStartInput<'a> {
/// Input supplied when the host completes a turn.
pub struct TurnStopInput<'a> {
/// Identifier for the thread containing this turn.
pub thread_id: ThreadId,
/// Identifier for the turn that is stopping.
pub turn_id: &'a str,
/// Store scoped to the host session runtime.
pub session_store: &'a ExtensionData,
/// Store scoped to this thread runtime.
@@ -33,10 +24,6 @@ pub struct TurnStopInput<'a> {
/// Input supplied when the host aborts a turn.
pub struct TurnAbortInput<'a> {
/// Identifier for the thread containing this turn.
pub thread_id: ThreadId,
/// Identifier for the turn that is aborting.
pub turn_id: &'a str,
/// Reason the host aborted the turn.
pub reason: TurnAbortReason,
/// Store scoped to the host session runtime.

View File

@@ -8,15 +8,24 @@ use std::sync::PoisonError;
type ErasedData = Arc<dyn Any + Send + Sync>;
/// Typed extension-owned data attached to one host object.
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct ExtensionData {
level_id: String,
entries: Mutex<HashMap<TypeId, ErasedData>>,
}
impl ExtensionData {
/// Creates an empty attachment map.
pub fn new() -> Self {
Self::default()
/// Creates an empty attachment map for one host-owned scope.
pub fn new(level_id: impl Into<String>) -> Self {
Self {
level_id: level_id.into(),
entries: Mutex::new(HashMap::new()),
}
}
/// Returns the host identity for the scope this data is attached to.
pub fn level_id(&self) -> &str {
&self.level_id
}
/// Returns the attached value of type `T`, if one exists.