[2 of 2] Finish moving goal runtime to extension (#26548)

## Stack

1. [#26547](https://github.com/openai/codex/pull/26547) - [1 of 2] Align
goal extension with core behavior
2. [#26548](https://github.com/openai/codex/pull/26548) - [2 of 2] Move
goal runtime to extension

## Why

This PR completes the switch of the goal behavior to the
extension-backed runtime and removes the old core goal implementation.

## What Changed

- Installs the goal extension for app-server `ThreadManager` sessions.
- Routes app-server thread goal `get`, `set`, and `clear` through
`GoalService`.
- Uses thread-idle lifecycle emission after goal resume and snapshot
ordering so the extension can decide whether to continue the goal.
- Forwards extension goal updates through a FIFO async app-server
notification path so backpressure does not drop them or reorder updates.
- Keeps review turns from enabling goal runtime behavior.
- Plans extension tools before dynamic tools so built-in goal tool names
keep their old precedence when goals are enabled.
- Removes the old core goal runtime, core goal tool handlers, and core
goal tool specs.
- Updates tests that were coupled to the core-owned goal runtime while
leaving the legacy `<goal_context>` compatibility path in core for old
threads.
- Removes the stale cargo-shear ignore now that `codex-goal-extension`
is used by the workspace.
- Keeps realtime event matching exhaustive after removing the old
goal-specific realtime text path.


## Validation

- Ran manual `/goal` runs in TUI. Validated time accounting matched
wall-clock time and goal lifecycle state transitions.
This commit is contained in:
Eric Traut
2026-06-05 14:17:30 -07:00
committed by GitHub
Unverified
parent 679cc08445
commit 479a14cf59
34 changed files with 280 additions and 3908 deletions
-42
View File
@@ -23,7 +23,6 @@ use tracing::warn;
use crate::config::Config;
use crate::context::ContextualUserFragment;
use crate::goals::GoalRuntimeEvent;
use crate::hook_runtime::inspect_pending_input;
use crate::hook_runtime::record_additional_contexts;
use crate::hook_runtime::record_pending_input;
@@ -342,15 +341,6 @@ impl Session {
.await
.clear_turn(&turn_context.sub_id);
if let Err(err) = self
.goal_runtime_apply(GoalRuntimeEvent::TurnStarted {
turn_context: turn_context.as_ref(),
token_usage: token_usage_at_turn_start.clone(),
})
.await
{
warn!("failed to apply goal runtime turn-start event: {err}");
}
let pending_items = self.input_queue.get_pending_input(&self.active_turn).await;
let turn_state = {
let mut active = self.active_turn.lock().await;
@@ -504,15 +494,6 @@ impl Session {
self.emit_turn_abort_lifecycle(reason.clone(), turn_context.extension_data.as_ref())
.await;
}
if (aborted_turn || reason == TurnAbortReason::Interrupted)
&& let Err(err) = self
.goal_runtime_apply(GoalRuntimeEvent::TaskAborted {
turn_context: turn_context.as_deref(),
})
.await
{
warn!("failed to apply goal runtime abort event: {err}");
}
if let Some(active_turn) = active_turn_to_clear {
// Let interrupted tasks observe cancellation before dropping pending approvals, or an
// in-flight approval wait can surface as a model-visible rejection before TurnAborted.
@@ -553,14 +534,6 @@ impl Session {
self.emit_turn_abort_lifecycle(reason.clone(), turn_context.extension_data.as_ref())
.await;
}
if let Err(err) = self
.goal_runtime_apply(GoalRuntimeEvent::TaskAborted {
turn_context: turn_context.as_deref(),
})
.await
{
warn!("failed to apply goal runtime abort event: {err}");
}
// Let interrupted tasks observe cancellation before dropping pending approvals, or an
// in-flight approval wait can surface as a model-visible rejection before TurnAborted.
self.input_queue.clear_pending(&active_turn).await;
@@ -760,15 +733,6 @@ impl Session {
});
self.emit_turn_stop_lifecycle(turn_context.extension_data.as_ref())
.await;
if let Err(err) = self
.goal_runtime_apply(GoalRuntimeEvent::TurnFinished {
turn_context: turn_context.as_ref(),
turn_completed: true,
})
.await
{
warn!("failed to apply goal runtime turn-finished event: {err}");
}
let event = EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: turn_context.sub_id.clone(),
last_agent_message,
@@ -798,12 +762,6 @@ impl Session {
if !cleared_active_turn {
return;
}
if let Err(err) = self
.goal_runtime_apply(GoalRuntimeEvent::MaybeContinueIfIdle)
.await
{
warn!("failed to apply goal runtime maybe-continue event: {err}");
}
self.emit_thread_idle_lifecycle_if_idle().await;
}