Skip to content

Conversation

@owenlin0
Copy link
Contributor

@owenlin0 owenlin0 commented Nov 4, 2025

Implements:

thread/list
thread/start
thread/resume
thread/archive

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:

  • thread/start and thread/resume automatically attaches a conversation listener internally, so clients don't have to make a separate AddConversationListener call like they do today.

For consistency, also updated model/list and feedback/upload (naming conventions, list API params).

@owenlin0 owenlin0 changed the title Owen/feature/thread apis [app-server] feat: v2 Thread APIs Nov 4, 2025
@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from 77d059e to 48d30c8 Compare November 4, 2025 19:41
@owenlin0 owenlin0 marked this pull request as ready for review November 4, 2025 20:10
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

pub enum ServerNotification {
/// NEW NOTIFICATIONS
#[serde(rename = "account/updated")]
#[ts(rename = "account/updated")]
#[strum(serialize = "account/updated")]
AccountUpdated(v2::AccountUpdatedNotification),
#[serde(rename = "account/rateLimits/updated")]
#[ts(rename = "account/rateLimits/updated")]
#[strum(serialize = "account/rateLimits/updated")]
AccountRateLimitsUpdated(RateLimitSnapshot),

P1 Badge Account rate-limit notification uses old payload shape

The notification enum still declares AccountRateLimitsUpdated(RateLimitSnapshot) yet the commit introduces a new AccountRateLimitsUpdatedNotification type (and tests expect a nested rateLimits object). The send-site continues to pass a raw RateLimitSnapshot, so the emitted JSON lacks the new wrapper and cannot match the updated schema/serialization test. Either the enum variant needs to carry AccountRateLimitsUpdatedNotification or the send logic must wrap the snapshot before serialization; as written the API and implementation are inconsistent and will not build alongside the new test.

ℹ️ 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".

Comment on lines +241 to +247
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider defining a custom attribute macro (in a proc-macro crate) so you can make this one line instead of three.

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 making sure everything has these annotations is a bit tedious, might do as a followup

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct Thread {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we expand this to evolve to contain the history and other metadata?

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.

Yeah will follow up with some additional fields that are useful here. We're pretty inconsistent with the old API.

I'm thinking model_provider, rollout_path, preview might be useful. And created_at and updated_at timestamp would be quite nice I think. https://linear.app/openai/issue/CLI-1377/expose-useful-fields-on-thread

// Auto-attach a conversation listener when starting a thread.
// Use the same behavior as the v1 API with experimental_raw_events=false.
if let Err(err) = self
.attach_conversation_listener(new_conv.conversation_id, false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

We auto-add the listener now, but when do we remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i asked codex about the VSCE codebase and it looks like it never calls RemoveConversationListener 😅

however, I think it'd be worth doing on thread/archive at least (will follow up)

@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from 79b6356 to 32716d7 Compare November 5, 2025 01:09
@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from 32716d7 to ee62e19 Compare November 5, 2025 05:30
@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from ee62e19 to 3445d53 Compare November 5, 2025 05:31
pub struct ListModelsResponse {
pub items: Vec<Model>,
pub struct ModelListResponse {
pub data: Vec<Model>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we name this models instead?

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 like the convention that the OpenAI APIs have where list APIs have a consistent top-level shape:

{
  data: <list of results>,
  next_cursor: string | null,
}

it's nice since no matter which List API you call, the results are always in data

Some(s) => serde_json::from_str::<RolloutCursor>(&format!("\"{s}\"")).ok(),
None => None,
};
let cursor_obj: Option<RolloutCursor> = cursor.as_ref().and_then(|s| parse_cursor(s));
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm surprised clippy didn't tell you to do:

Suggested change
let cursor_obj: Option<RolloutCursor> = cursor.as_ref().and_then(|s| parse_cursor(s));
let cursor_obj: Option<RolloutCursor> = cursor.as_ref().and_then(parse_cursor);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

interesting, seems like that should work but getting this error:

type mismatch in function arguments
expected function signature `fn(&std::string::String) -> _`
   found function signature `fn(&str) -> _`rustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20[0]?0#file:///Users/owen/repos/codex/codex-rs/app-server/src/codex_message_processor.rs)

@owenlin0 owenlin0 enabled auto-merge (squash) November 5, 2025 19:45
@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from 047b89b to 3f5b1c8 Compare November 5, 2025 20:10
@owenlin0 owenlin0 force-pushed the owen/feature/thread-apis branch from 3f5b1c8 to 5e45b6c Compare November 5, 2025 20:14
@owenlin0 owenlin0 merged commit 2ab1650 into main Nov 5, 2025
25 checks passed
@owenlin0 owenlin0 deleted the owen/feature/thread-apis branch November 5, 2025 20:28
@github-actions github-actions bot locked and limited conversation to collaborators Nov 5, 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.

4 participants