mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add goal app-server API (2 / 5) (#18074)
Adds the app-server v2 goal API on top of the persisted goal state from PR 1. ## Why Clients need a stable app-server surface for reading and controlling materialized thread goals before the model tools and TUI can use them. Goal changes also need to be observable by app-server clients, including clients that resume an existing thread. ## What changed - Added v2 `thread/goal/get`, `thread/goal/set`, and `thread/goal/clear` RPCs for materialized threads. - Added `thread/goal/updated` and `thread/goal/cleared` notifications so clients can keep local goal state in sync. - Added resume/snapshot wiring so reconnecting clients see the current goal state for a thread. - Added app-server handlers that reconcile persisted rollout state before direct goal mutations. - Updated the app-server README plus generated JSON and TypeScript schema fixtures for the new API surface. ## Verification - Added app-server v2 coverage for goal get/set/clear behavior, notification emission, resume snapshots, and non-local thread-store interactions.
This commit is contained in:
committed by
GitHub
Unverified
parent
0ee737cea6
commit
6c874f9b34
@@ -285,6 +285,21 @@ client_request_definitions! {
|
||||
params: v2::ThreadSetNameParams,
|
||||
response: v2::ThreadSetNameResponse,
|
||||
},
|
||||
#[experimental("thread/goal/set")]
|
||||
ThreadGoalSet => "thread/goal/set" {
|
||||
params: v2::ThreadGoalSetParams,
|
||||
response: v2::ThreadGoalSetResponse,
|
||||
},
|
||||
#[experimental("thread/goal/get")]
|
||||
ThreadGoalGet => "thread/goal/get" {
|
||||
params: v2::ThreadGoalGetParams,
|
||||
response: v2::ThreadGoalGetResponse,
|
||||
},
|
||||
#[experimental("thread/goal/clear")]
|
||||
ThreadGoalClear => "thread/goal/clear" {
|
||||
params: v2::ThreadGoalClearParams,
|
||||
response: v2::ThreadGoalClearResponse,
|
||||
},
|
||||
ThreadMetadataUpdate => "thread/metadata/update" {
|
||||
params: v2::ThreadMetadataUpdateParams,
|
||||
response: v2::ThreadMetadataUpdateResponse,
|
||||
@@ -1027,6 +1042,10 @@ server_notification_definitions! {
|
||||
ThreadClosed => "thread/closed" (v2::ThreadClosedNotification),
|
||||
SkillsChanged => "skills/changed" (v2::SkillsChangedNotification),
|
||||
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
|
||||
#[experimental("thread/goal/updated")]
|
||||
ThreadGoalUpdated => "thread/goal/updated" (v2::ThreadGoalUpdatedNotification),
|
||||
#[experimental("thread/goal/cleared")]
|
||||
ThreadGoalCleared => "thread/goal/cleared" (v2::ThreadGoalClearedNotification),
|
||||
ThreadTokenUsageUpdated => "thread/tokenUsage/updated" (v2::ThreadTokenUsageUpdatedNotification),
|
||||
TurnStarted => "turn/started" (v2::TurnStartedNotification),
|
||||
HookStarted => "hook/started" (v2::HookStartedNotification),
|
||||
@@ -2046,6 +2065,76 @@ mod tests {
|
||||
let reason = crate::experimental_api::ExperimentalApi::experimental_reason(&request);
|
||||
assert_eq!(reason, Some("thread/realtime/start"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_goal_methods_are_marked_experimental() {
|
||||
let set_request = ClientRequest::ThreadGoalSet {
|
||||
request_id: RequestId::Integer(1),
|
||||
params: v2::ThreadGoalSetParams {
|
||||
thread_id: "thr_123".to_string(),
|
||||
objective: Some("ship goal mode".to_string()),
|
||||
status: Some(v2::ThreadGoalStatus::Active),
|
||||
token_budget: Some(Some(10_000)),
|
||||
},
|
||||
};
|
||||
let get_request = ClientRequest::ThreadGoalGet {
|
||||
request_id: RequestId::Integer(2),
|
||||
params: v2::ThreadGoalGetParams {
|
||||
thread_id: "thr_123".to_string(),
|
||||
},
|
||||
};
|
||||
let clear_request = ClientRequest::ThreadGoalClear {
|
||||
request_id: RequestId::Integer(3),
|
||||
params: v2::ThreadGoalClearParams {
|
||||
thread_id: "thr_123".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(&set_request),
|
||||
Some("thread/goal/set")
|
||||
);
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(&get_request),
|
||||
Some("thread/goal/get")
|
||||
);
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(&clear_request),
|
||||
Some("thread/goal/clear")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_goal_notifications_are_marked_experimental() {
|
||||
let goal = v2::ThreadGoal {
|
||||
thread_id: "thr_123".to_string(),
|
||||
objective: "ship goal mode".to_string(),
|
||||
status: v2::ThreadGoalStatus::Active,
|
||||
token_budget: Some(10_000),
|
||||
tokens_used: 123,
|
||||
time_used_seconds: 45,
|
||||
created_at: 1_700_000_000,
|
||||
updated_at: 1_700_000_123,
|
||||
};
|
||||
let updated = ServerNotification::ThreadGoalUpdated(v2::ThreadGoalUpdatedNotification {
|
||||
thread_id: "thr_123".to_string(),
|
||||
turn_id: None,
|
||||
goal,
|
||||
});
|
||||
let cleared = ServerNotification::ThreadGoalCleared(v2::ThreadGoalClearedNotification {
|
||||
thread_id: "thr_123".to_string(),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(&updated),
|
||||
Some("thread/goal/updated")
|
||||
);
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(&cleared),
|
||||
Some("thread/goal/cleared")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_realtime_started_notification_is_marked_experimental() {
|
||||
let notification =
|
||||
|
||||
Reference in New Issue
Block a user