mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
## Why #24744 introduced the thread idle lifecycle hook so idle continuation can be owned by lifecycle contributors instead of hard-coded goal runtime plumbing. Task completion still called `goal_runtime_apply(GoalRuntimeEvent::MaybeContinueIfIdle)` directly, so the post-turn idle transition remained goal-specific and did not notify generic thread lifecycle contributors. ## What Changed - Add `Session::emit_thread_idle_lifecycle_if_idle()` to gate idle emission on both no active turn and no queued trigger-turn mailbox work. - Call that helper when a task clears the active turn, replacing the direct `GoalRuntimeEvent::MaybeContinueIfIdle` path. - Cover the behavior with `codex-core` session tests for emitting after task completion and suppressing idle emission while trigger-turn mailbox work is pending. ## Verification - New tests in `core/src/session/tests.rs` exercise the idle lifecycle emission and trigger-turn mailbox guard.
93 lines
3.4 KiB
Rust
93 lines
3.4 KiB
Rust
use codex_extension_api::ExtensionData;
|
|
use codex_protocol::protocol::CodexErrorInfo;
|
|
use codex_protocol::protocol::TokenUsage;
|
|
use codex_protocol::protocol::TurnAbortReason;
|
|
|
|
use crate::session::session::Session;
|
|
use crate::session::turn_context::TurnContext;
|
|
|
|
impl Session {
|
|
pub(super) async fn emit_turn_start_lifecycle(
|
|
&self,
|
|
turn_context: &TurnContext,
|
|
token_usage_at_turn_start: &TokenUsage,
|
|
) {
|
|
for contributor in self.services.extensions.turn_lifecycle_contributors() {
|
|
contributor
|
|
.on_turn_start(codex_extension_api::TurnStartInput {
|
|
turn_id: turn_context.sub_id.as_str(),
|
|
collaboration_mode: &turn_context.collaboration_mode,
|
|
token_usage_at_turn_start,
|
|
session_store: &self.services.session_extension_data,
|
|
thread_store: &self.services.thread_extension_data,
|
|
turn_store: turn_context.extension_data.as_ref(),
|
|
})
|
|
.await;
|
|
}
|
|
}
|
|
|
|
pub(super) async fn emit_turn_stop_lifecycle(&self, turn_store: &ExtensionData) {
|
|
for contributor in self.services.extensions.turn_lifecycle_contributors() {
|
|
contributor
|
|
.on_turn_stop(codex_extension_api::TurnStopInput {
|
|
session_store: &self.services.session_extension_data,
|
|
thread_store: &self.services.thread_extension_data,
|
|
turn_store,
|
|
})
|
|
.await;
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn emit_thread_idle_lifecycle_if_idle(&self) {
|
|
if self.active_turn.lock().await.is_some()
|
|
|| self.input_queue.has_trigger_turn_mailbox_items().await
|
|
{
|
|
return;
|
|
}
|
|
|
|
for contributor in self.services.extensions.thread_lifecycle_contributors() {
|
|
contributor
|
|
.on_thread_idle(codex_extension_api::ThreadIdleInput {
|
|
session_store: &self.services.session_extension_data,
|
|
thread_store: &self.services.thread_extension_data,
|
|
})
|
|
.await;
|
|
}
|
|
}
|
|
|
|
pub(super) async fn emit_turn_abort_lifecycle(
|
|
&self,
|
|
reason: TurnAbortReason,
|
|
turn_store: &ExtensionData,
|
|
) {
|
|
for contributor in self.services.extensions.turn_lifecycle_contributors() {
|
|
contributor
|
|
.on_turn_abort(codex_extension_api::TurnAbortInput {
|
|
reason: reason.clone(),
|
|
session_store: &self.services.session_extension_data,
|
|
thread_store: &self.services.thread_extension_data,
|
|
turn_store,
|
|
})
|
|
.await;
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn emit_turn_error_lifecycle(
|
|
&self,
|
|
turn_context: &TurnContext,
|
|
error: CodexErrorInfo,
|
|
) {
|
|
for contributor in self.services.extensions.turn_lifecycle_contributors() {
|
|
contributor
|
|
.on_turn_error(codex_extension_api::TurnErrorInput {
|
|
turn_id: turn_context.sub_id.as_str(),
|
|
error: error.clone(),
|
|
session_store: &self.services.session_extension_data,
|
|
thread_store: &self.services.thread_extension_data,
|
|
turn_store: turn_context.extension_data.as_ref(),
|
|
})
|
|
.await;
|
|
}
|
|
}
|
|
}
|