mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[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:
committed by
GitHub
Unverified
parent
679cc08445
commit
479a14cf59
@@ -17,6 +17,7 @@ use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex as StdMutex;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -44,8 +45,9 @@ pub(crate) struct PendingThreadResumeRequest {
|
||||
pub(crate) enum ThreadListenerCommand {
|
||||
// SendThreadResumeResponse is used to resume an already running thread by sending the thread's history to the client and atomically subscribing for new updates.
|
||||
SendThreadResumeResponse(Box<PendingThreadResumeRequest>),
|
||||
// EmitThreadGoalUpdated is used to order app-server goal updates with running-thread resume responses.
|
||||
// EmitThreadGoalUpdated is used to order goal updates with running-thread resume responses and goal clears.
|
||||
EmitThreadGoalUpdated {
|
||||
turn_id: Option<String>,
|
||||
goal: ThreadGoal,
|
||||
},
|
||||
// EmitThreadGoalCleared is used to order app-server goal clears with running-thread resume responses.
|
||||
@@ -284,6 +286,10 @@ pub(crate) struct ConnectionCapabilities {
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct ThreadStateManager {
|
||||
state: Arc<Mutex<ThreadStateManagerInner>>,
|
||||
// Extension event sinks are synchronous, so they need an await-free way to
|
||||
// enqueue work on the active per-thread listener.
|
||||
listener_commands:
|
||||
Arc<StdMutex<HashMap<ThreadId, mpsc::UnboundedSender<ThreadListenerCommand>>>>,
|
||||
}
|
||||
|
||||
impl ThreadStateManager {
|
||||
@@ -337,6 +343,35 @@ impl ThreadStateManager {
|
||||
state.threads.entry(thread_id).or_default().state.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn current_listener_command_tx(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
) -> Option<mpsc::UnboundedSender<ThreadListenerCommand>> {
|
||||
self.listener_commands
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.get(&thread_id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn register_listener_command_tx(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
tx: mpsc::UnboundedSender<ThreadListenerCommand>,
|
||||
) {
|
||||
self.listener_commands
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.insert(thread_id, tx);
|
||||
}
|
||||
|
||||
pub(crate) fn unregister_listener_command_tx(&self, thread_id: ThreadId) {
|
||||
self.listener_commands
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.remove(&thread_id);
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_thread_state(&self, thread_id: ThreadId) {
|
||||
let thread_state = {
|
||||
let mut state = self.state.lock().await;
|
||||
@@ -350,6 +385,7 @@ impl ThreadStateManager {
|
||||
});
|
||||
thread_state
|
||||
};
|
||||
self.unregister_listener_command_tx(thread_id);
|
||||
|
||||
if let Some(thread_state) = thread_state {
|
||||
let mut thread_state = thread_state.lock().await;
|
||||
@@ -375,6 +411,7 @@ impl ThreadStateManager {
|
||||
};
|
||||
|
||||
for (thread_id, thread_state) in thread_states {
|
||||
self.unregister_listener_command_tx(thread_id);
|
||||
let mut thread_state = thread_state.lock().await;
|
||||
tracing::debug!(
|
||||
thread_id = %thread_id,
|
||||
|
||||
Reference in New Issue
Block a user