mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add goal core runtime (4 / 5) (#18076)
Adds the core runtime behavior for active goals on top of the model tools from PR 3. ## Why A long-running goal should be a core runtime concern, not something every client has to implement. Core owns the turn lifecycle, tool completion boundaries, interruptions, resume behavior, and token usage, so it is the right place to account progress, enforce budgets, and decide when to continue work. ## What changed - Centralized goal lifecycle side effects behind `Session::goal_runtime_apply(GoalRuntimeEvent::...)`. - Starts goal continuation turns only when the session is idle; pending user input and mailbox work take priority. - Accounts token and wall-clock usage at turn, tool, mutation, interrupt, and resume boundaries; `get_thread_goal` remains read-only. - Preserves sub-second wall-clock remainder across accounting boundaries so long-running goals do not drift downward over time. - Treats token budget exhaustion as a soft stop by marking the goal `budget_limited` and injecting wrap-up steering instead of aborting the active turn. - Suppresses budget steering when `update_goal` marks a goal complete. - Pauses active goals on interrupt and auto-reactivates paused goals when a thread resumes outside plan mode. - Suppresses repeated automatic continuation when a continuation turn makes no tool calls. - Added continuation and budget-limit prompt templates. ## Verification - Added focused core coverage for continuation scheduling, accounting boundaries, budget-limit steering, completion accounting, interrupt pause behavior, resume auto-activation, and wall-clock remainder accounting.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use crate::agent::AgentStatus;
|
||||
use crate::config::ConstraintResult;
|
||||
use crate::file_watcher::WatchRegistration;
|
||||
use crate::goals::GoalRuntimeEvent;
|
||||
use crate::session::Codex;
|
||||
use crate::session::SessionSettingsUpdate;
|
||||
use crate::session::SteerInputError;
|
||||
@@ -103,6 +104,53 @@ impl CodexThread {
|
||||
self.codex.shutdown_and_wait().await
|
||||
}
|
||||
|
||||
pub async fn apply_goal_resume_runtime_effects(&self) -> anyhow::Result<()> {
|
||||
self.codex
|
||||
.session
|
||||
.goal_runtime_apply(GoalRuntimeEvent::ThreadResumed)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn continue_active_goal_if_idle(&self) -> anyhow::Result<()> {
|
||||
self.codex
|
||||
.session
|
||||
.goal_runtime_apply(GoalRuntimeEvent::MaybeContinueIfIdle)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn prepare_external_goal_mutation(&self) {
|
||||
if let Err(err) = self
|
||||
.codex
|
||||
.session
|
||||
.goal_runtime_apply(GoalRuntimeEvent::ExternalMutationStarting)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("failed to prepare external goal mutation: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn apply_external_goal_set(&self, status: codex_state::ThreadGoalStatus) {
|
||||
if let Err(err) = self
|
||||
.codex
|
||||
.session
|
||||
.goal_runtime_apply(GoalRuntimeEvent::ExternalSet { status })
|
||||
.await
|
||||
{
|
||||
tracing::warn!("failed to apply external goal status runtime effects: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn apply_external_goal_clear(&self) {
|
||||
if let Err(err) = self
|
||||
.codex
|
||||
.session
|
||||
.goal_runtime_apply(GoalRuntimeEvent::ExternalClear)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("failed to apply external goal clear runtime effects: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub async fn ensure_rollout_materialized(&self) {
|
||||
self.codex.session.ensure_rollout_materialized().await;
|
||||
|
||||
Reference in New Issue
Block a user