Skip to content

Conversation

@owenlin0
Copy link
Contributor

@owenlin0 owenlin0 commented Nov 4, 2025

Implements:

turn/start
turn/interrupt

along with their integration tests. These are relatively light wrappers around the existing core logic, and changes to core logic are minimal.

However, an improvement made for developer ergonomics:

  • turn/start replaces both SendUserMessage (no turn overrides) and SendUserTurn (can override model, approval policy, etc.)

@owenlin0 owenlin0 marked this pull request as ready for review November 4, 2025 20:11
Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@owenlin0 owenlin0 force-pushed the owen/turn_apis branch 2 times, most recently from ae02487 to 90b3e74 Compare November 5, 2025 18:58
},
#[serde(rename = "turn/start")]
#[ts(rename = "turn/start")]
TurnStart {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while we are here, how do we want to implement best of N parameters, here?

should TurnStartResponse support list of turns as response?

cc: @wiltzius-openai @gpeal

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut says to let bon live a level above to avoid making a single API too complex.

bon in the most basic sense is a for loop around TurnStart. However, it may get more involved over time where you have a second layer that analyzes the turns and then kicks off another, etc and not coupling that to this API gives us more options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I do think there's something really nice about keeping TurnStart as simple as possible and have it be a primitive

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apanasenko-oai do you all need first-class BoN support in app-server for codex cloud?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue with having BoN live outside the API is that we still need to link threads/turns. That means either calling a special API on the app-server to handle the linking, or managing the storage externally. But if we manage it externally, we end up having to handle cloud and local separately, which adds complexity.

I agree we should keep the API simple, but BoN feels like a core part of the product for both local and cloud environments.

Copy link
Contributor Author

@owenlin0 owenlin0 Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think the API should make BoN easy, but needs some design work 🤔

maybe Threads need to start looking like a Tree instead of a linear list of Turns, for example (depending on the level of visibility / control we want to give to the user).

use tracing::warn;
use uuid::Uuid;

type PendingInterruptQueue = Vec<(RequestId, ApiVersion)>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since turn/interrupt (and the old API) sends a response upon consume an EventMsg::TurnAborted from codex core, we need to store the API version along with the pending interrupt so we know whether to return a V1 or a V2 response

#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TurnInterruptParams {
pub thread_id: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a thread_id globally unique, btw?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turn IDs are not actually and I think we want them to be, otherwise we will always need (thread_id, turn_id) to uniquely identify a Turn

filed: https://linear.app/openai/issue/CLI-1432/make-turn-ids-unique

message: format!("failed to list conversations: {err}"),
data: None,
};
Ok(result) => result,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would handle the send_response() in here and then you can omit the return;.

r#"
model = "mock-model"
approval_policy = "never"
sandbox_mode = "workspace-write"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, read-only works!


#[tokio::test]
async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
if env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip_if_no_network!(); ?

r#"
model = "mock-model"
approval_policy = "{approval_policy}"
sandbox_mode = "workspace-write"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it write? or is it important to test the override?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests still pass with read-only 👍

@owenlin0 owenlin0 enabled auto-merge (squash) November 6, 2025 16:19
@owenlin0 owenlin0 merged commit 6582554 into main Nov 6, 2025
25 checks passed
@owenlin0 owenlin0 deleted the owen/turn_apis branch November 6, 2025 16:36
@github-actions github-actions bot locked and limited conversation to collaborators Nov 6, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants