mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
feat: handle goal usage limits in goal extension (#24628)
## Why The extracted goal runtime needs a host-callable path for turns that stop because the workspace usage limit is reached. In that case, any in-turn goal progress should be accounted before the goal becomes terminal, and active goal accounting must be cleared so later tool-finish or turn-stop handling does not keep charging usage to a stopped goal. ## What changed - Adds `GoalRuntimeHandle::usage_limit_active_goal_for_turn`, which accounts current active-goal progress, marks the active or budget-limited thread goal as `UsageLimited`, records terminal metrics when the status changes, clears active goal accounting, and emits the updated goal event. - Covers both active and budget-limited goals in `ext/goal/tests/goal_extension_backend.rs`, including the invariant that later token/tool events do not add usage after the goal has been usage-limited. ## Testing - Added `usage_limit_active_goal_accounts_progress_and_clears_accounting`. - Added `usage_limit_budget_limited_goal_accounts_remaining_progress`.
This commit is contained in:
1 parent
ca2e343f65
commit
bc005029bd
3 files changed
+303
-1
No files matched your search
@@ -192,6 +192,54 @@ impl GoalRuntimeHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn usage_limit_active_goal_for_turn(&self, turn_id: &str) -> Result<(), String> {
|
||||
if !self.is_enabled() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !self
|
||||
.inner
|
||||
.accounting_state
|
||||
.turn_is_current_active_goal(turn_id)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let progress_event_id = format!("{turn_id}:usage-limit-progress");
|
||||
self.account_active_goal_progress(
|
||||
turn_id,
|
||||
progress_event_id.as_str(),
|
||||
codex_state::GoalAccountingMode::ActiveOnly,
|
||||
BudgetLimitedGoalDisposition::ClearActive,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let previous_status = self
|
||||
.current_goal_status_for_metrics(/*expected_goal_id*/ None)
|
||||
.await?;
|
||||
let Some(goal) = self
|
||||
.inner
|
||||
.state_dbs
|
||||
.thread_goals()
|
||||
.usage_limit_active_thread_goal(self.thread_id())
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
self.inner
|
||||
.metrics
|
||||
.record_terminal_if_status_changed(previous_status, &goal);
|
||||
self.inner.accounting_state.clear_active_goal();
|
||||
let goal = protocol_goal_from_state(goal);
|
||||
self.inner.event_emitter.thread_goal_updated(
|
||||
format!("{turn_id}:usage-limit"),
|
||||
Some(turn_id.to_string()),
|
||||
goal,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn restore_after_resume(&self) -> Result<(), String> {
|
||||
if !self.is_enabled() {
|
||||
return Ok(());
|
||||
|
||||
Reference in new issue
Block a user