mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why Goal creation and completion are moving through the goal extension, but the rest of Codex still observes goal state through `ThreadGoalUpdated` events. Without an event from the extension-owned tool path, a model-initiated `create_goal` or `update_goal` can mutate the backend and return a tool result while app-server and TUI listeners miss the goal state transition. ## What changed - Added `GoalEventEmitter` as a small wrapper around the host `ExtensionEventSink` to build `EventMsg::ThreadGoalUpdated` events for goal updates. - Threaded the registry event sink into `GoalExtension` and the `GoalToolExecutor`s created by the extension. The public `GoalExtension::new` constructor keeps a `NoopExtensionEventSink` fallback for standalone use. - Emitted a goal update after successful `create_goal` and `update_goal` tool calls. Until `ToolCall` exposes the current turn submission id, these events use the tool call id as the event id and leave `turn_id` unset. Relevant code: - [`GoalEventEmitter::thread_goal_updated`](https://github.com/openai/codex/blob/1fe2d73890df9a50996f67f705d4da4cc3d4b866/codex-rs/ext/goal/src/events.rs#L19-L32) - [`GoalToolExecutor` emission points](https://github.com/openai/codex/blob/1fe2d73890df9a50996f67f705d4da4cc3d4b866/codex-rs/ext/goal/src/tool.rs#L161-L190) ## Testing - `cargo test -p codex-goal-extension`
35 lines
874 B
Rust
35 lines
874 B
Rust
use std::sync::Arc;
|
|
|
|
use codex_extension_api::ExtensionEventSink;
|
|
use codex_protocol::protocol::Event;
|
|
use codex_protocol::protocol::EventMsg;
|
|
use codex_protocol::protocol::ThreadGoal;
|
|
use codex_protocol::protocol::ThreadGoalUpdatedEvent;
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct GoalEventEmitter {
|
|
sink: Arc<dyn ExtensionEventSink>,
|
|
}
|
|
|
|
impl GoalEventEmitter {
|
|
pub(crate) fn new(sink: Arc<dyn ExtensionEventSink>) -> Self {
|
|
Self { sink }
|
|
}
|
|
|
|
pub(crate) fn thread_goal_updated(
|
|
&self,
|
|
event_id: impl Into<String>,
|
|
turn_id: Option<String>,
|
|
goal: ThreadGoal,
|
|
) {
|
|
self.sink.emit(Event {
|
|
id: event_id.into(),
|
|
msg: EventMsg::ThreadGoalUpdated(ThreadGoalUpdatedEvent {
|
|
thread_id: goal.thread_id,
|
|
turn_id,
|
|
goal,
|
|
}),
|
|
});
|
|
}
|
|
}
|