mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add goal extension idle continuation (#25060)
## Why The goal extension needs a way to resume an active goal after the thread becomes idle, but the old core goal runtime should not be refactored as part of this step. The missing piece is a small core-owned turn-start primitive: let an extension ask for a normal model turn only when the thread is idle, and otherwise fail without injecting into whatever is currently active. ## What Changed - Adds `CodexThread::try_start_turn_if_idle(...)` as the narrow extension-facing primitive for synthetic idle work. - Implements the session side so it refuses to start when: - the provided input is empty, - the session is in plan mode, - a turn is already active, or - trigger-turn mailbox work is pending. - Gives trigger-turn mailbox work priority if it appears while the idle turn is being prepared. - Wires `GoalExtension::on_thread_idle` to read the active persisted goal and submit the continuation prompt through this idle-only primitive. - Keeps the legacy core goal continuation implementation in place instead of folding it into this PR. ## Behavior This is intentionally best-effort. If `try_start_turn_if_idle` observes that the thread is not idle, or that higher-priority mailbox work should run first, it returns the input to the caller. The goal extension drops that continuation prompt and waits for a future idle opportunity instead of injecting stale synthetic goal text into an active turn. ## Validation - `just test -p codex-core try_start_turn_if_idle_rejects_active_turn_without_injecting` - `just test -p codex-goal-extension`
This commit is contained in:
@@ -12,6 +12,7 @@ use crate::accounting::BudgetLimitedGoalDisposition;
|
||||
use crate::accounting::GoalAccountingState;
|
||||
use crate::events::GoalEventEmitter;
|
||||
use crate::metrics::GoalMetrics;
|
||||
use crate::steering::continuation_steering_item;
|
||||
use crate::steering::objective_updated_steering_item;
|
||||
use crate::tool::protocol_goal_from_state;
|
||||
|
||||
@@ -275,6 +276,57 @@ impl GoalRuntimeHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn continue_if_idle(&self) -> Result<(), String> {
|
||||
if !self.tools_visible() {
|
||||
self.inner.accounting_state.clear_active_goal();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(goal) = self
|
||||
.inner
|
||||
.state_dbs
|
||||
.thread_goals()
|
||||
.get_thread_goal(self.thread_id())
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
else {
|
||||
self.inner.accounting_state.clear_active_goal();
|
||||
return Ok(());
|
||||
};
|
||||
if goal.status != codex_state::ThreadGoalStatus::Active {
|
||||
self.inner.accounting_state.clear_active_goal();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let item = continuation_steering_item(&protocol_goal_from_state(goal));
|
||||
let Some(thread_manager) = self.inner.thread_manager.upgrade() else {
|
||||
tracing::debug!("skipping goal continuation because thread manager is unavailable");
|
||||
return Ok(());
|
||||
};
|
||||
let Ok(thread) = thread_manager.get_thread(self.inner.thread_id).await else {
|
||||
tracing::debug!("skipping goal continuation because live thread is unavailable");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if thread.try_start_turn_if_idle(vec![item]).await.is_err() {
|
||||
tracing::debug!("skipping goal continuation because the thread is no longer idle");
|
||||
}
|
||||
|
||||
let current_turn_is_goal_active = self
|
||||
.inner
|
||||
.accounting_state
|
||||
.current_turn_id()
|
||||
.is_some_and(|turn_id| {
|
||||
self.inner
|
||||
.accounting_state
|
||||
.turn_is_current_active_goal(turn_id.as_str())
|
||||
});
|
||||
if !current_turn_is_goal_active {
|
||||
self.inner.accounting_state.clear_active_goal();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn inject_active_turn_steering(&self, item: ResponseItem) {
|
||||
let Some(thread_manager) = self.inner.thread_manager.upgrade() else {
|
||||
tracing::debug!("skipping goal steering because thread manager is unavailable");
|
||||
|
||||
Reference in New Issue
Block a user