[codex] support external clock sleeps - #29923
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37358928b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e96cf3e82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .send_server_notification_to_connections( | ||
| &[connection_id], | ||
| ServerNotification::CurrentTimeSleep(CurrentTimeSleepParams { |
There was a problem hiding this comment.
Fail fast when sleep notifications are filtered
If the subscribed client initialized without experimentalApi (or opted out of currentTime/sleep), the transport drops this experimental ServerNotification before it is written, but notify_external_sleep still returns Ok and the sleep future then waits only for a later currentTime/wake. In that configuration a model clock.sleep call hangs indefinitely instead of reporting that the selected client cannot handle external sleeps; please verify the chosen connection can receive this notification, or use an acknowledged request/bounded wait.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
not a serious issue for the limited use case here
| result.map_err(|err| { | ||
| FunctionCallError::Fatal(format!("failed to sleep: {err:#}")) | ||
| })?; |
There was a problem hiding this comment.
Complete the sleep item on provider errors
With an external clock, time_provider.sleep can now fail after emit_turn_item_started has already published the sleep item (for example, no unambiguous subscribed clock client within the 5s lookup). This ? returns a fatal tool error before emit_turn_item_completed runs, leaving clients with an in-progress sleep item in the live event stream even though the turn fails. Please ensure the started sleep item is completed or failed on this error path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
not a serious issue here either
15eb0ba to
e7572c1
Compare
5796b00 to
946eccb
Compare
258b1d9 to
0ea6315
Compare
0ea6315 to
950b702
Compare
| let deadline = Instant::now() + EXTERNAL_CLOCK_REQUEST_TIMEOUT; | ||
| let connection_id = | ||
| wait_for_current_time_connection(thread_state_manager, thread_id, deadline).await?; | ||
| outgoing |
There was a problem hiding this comment.
This notification is required control flow, but both in-process delivery tiers still classify it as best-effort. Either bounded queue can drop it, after which this future waits forever for a wake the client never saw
There was a problem hiding this comment.
notiffication should be considered required delivery now (so the bounded queue should wait until delivery).
| ClientRequest::Initialize { .. } => { | ||
| panic!("Initialize should be handled before initialized request dispatch"); | ||
| } | ||
| ClientRequest::CurrentTimeWake { params, .. } => { |
There was a problem hiding this comment.
We have the caller’s connection_id here but throw it away!
Since the sleep was sent to one specific external-clock connection, the pending entry needsto record that owner and reject wakes from other connections. Otherwise a different client can complete the sleep without advancing the clock used by later currentTime/read calls
There was a problem hiding this comment.
done! was originally building for specifically the 1 client case. Agreed its better we account for the general case here 👍
| .await??; | ||
| let _: CurrentTimeWakeResponse = to_response(wake_response)?; | ||
|
|
||
| respond_to_current_time_read(&mut app_server, &thread.id, CURRENT_TIME_AT + 1).await?; |
There was a problem hiding this comment.
This requests a 12-hour sleep but advances the fake clock by one second, and we never prove the turn was still blocked before wake
jif-oai
left a comment
There was a problem hiding this comment.
I’m still pretty unconvinced by this shape. currentTime/sleep is required control flow, but modeling it as a notification plus a separate currentTime/wake request makes us rebuild correlation, delivery, cancellation, connection affinity, and stale-wake semantics by hand.
I can imagine the edge cases exploding here.
his should be a server-initiated request that remains pending until the client advances its clock and responds. What do you think?
| { | ||
| bail!("external sleep can only be woken by the client that received it"); | ||
| } | ||
| let pending_sleep = entries.remove(key); |
There was a problem hiding this comment.
NIT: This also ACKs a typo or wrong threadId/sleepId
andmis
left a comment
There was a problem hiding this comment.
(Leaving comments in slack.)
|
Closing as superseded by #30113, which supports external clock sleeps by polling the existing currentTime/read method without adding a separate sleep/wake protocol. |
Summary
currentTime/sleepnotification andcurrentTime/wakerequest for external clocksKey Decision: I've set currentTime/sleep as a notification, but if we want to be completely safe, I think the right behavior is expect the app server client to "ack" back via {} - but i don't believe there's any existing precedent for this for app server -> client methods (only client -> app server)