Skip to content

Handle session/setTitle and emit SessionInfoUpdate#286

Draft
daniel-agentee wants to merge 7 commits into
zed-industries:mainfrom
daniel-agentee:acp-set-title-handler
Draft

Handle session/setTitle and emit SessionInfoUpdate#286
daniel-agentee wants to merge 7 commits into
zed-industries:mainfrom
daniel-agentee:acp-set-title-handler

Conversation

@daniel-agentee
Copy link
Copy Markdown

@daniel-agentee daniel-agentee commented May 14, 2026

Summary

Implements the agent side of the new ACP session/setTitle method so ACP clients (e.g. Zed) can persist a session rename through codex-acp. After a successful rename, codex-acp emits a SessionInfoUpdate notification so the client can repaint its session list inline — no restart, no session/list round-trip required.

Why

Zed's agent panel already has a title editor for ACP sessions (zed#54689), but it can't persist anything for ACP threads because there was no protocol method to push the new title down. With the protocol method now defined upstream, codex-acp needs a handler that:

  1. Updates the SQLite state index (so session/list returns the new title even after a fresh process)
  2. Writes a custom-title entry to the JSONL rollout (so find_thread_name_by_id returns the new value)
  3. Notifies the client so the title in the sidebar updates immediately

session/list also needs to consult the rollout's custom-title entries when assembling each session's title, otherwise the rename is invisible until the next codex restart.

What's in this PR

  • codex_agent.rs — registers a SetSessionTitleRequest handler. For loaded sessions it dispatches to Thread::set_title; for historical/listed sessions that are not currently loaded, it updates the SQLite/index state directly and emits the same SessionInfoUpdate notification.
  • listSessions now consults find_thread_name_by_id first and falls back to the formatted first user message.
  • thread.rs — adds Thread::set_title + ThreadMessage::SetTitle + ThreadActor::handle_set_title, which calls append_thread_name(codex_home, thread_id, title) to persist the rename. Then emits SessionUpdate::SessionInfoUpdate { title } so the client knows.
  • New unit coverage for both paths:
    • test_set_session_title_renames_underlying_thread covers the loaded-thread actor path, JSONL persistence, and SessionInfoUpdate emit.
    • set_session_title_persists_unloaded_session covers renaming a listed/historical session that is not present in the live sessions map.

Tests

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo check --locked
cargo build --locked
cargo test --locked --lib
bash npm/testing/validate.sh
node npm/testing/test-platform-detection.js

23 lib tests pass on the rebased branch (269c0d3).

Upstream dependency chain

This PR sits on top of two upstream PRs that must merge first. To keep the build green here in the meantime, Cargo.toml carries a temporary [patch.crates-io] block pointing at branch revs on my fork — see the inline comment in Cargo.toml for removal steps once releases ship.

  1. Schemaagentclientprotocol/agent-client-protocol#1199 adds SetSessionTitleRequest / SetSessionTitleResponse to the protocol schema. Patched in here via agent-client-protocol-schema = { git = ..., rev = b9739d6 }.
  2. Rust SDK routingagentclientprotocol/rust-sdk#164 wires the new schema types into the agent-side dispatch tables. Patched in here via agent-client-protocol = { git = ..., rev = 34523b3 }.
  3. This PR — uses the new types to handle the request and emit the repaint notification.

Once #1199 and rust-sdk#164 merge and an agent-client-protocol release ships with session/setTitle, drop the temporary patch entries and bump agent-client-protocol to the released version.

Companion PRs

Draft

Drafted while waiting on the two upstream PRs above. Ready for design review on the codex-acp side; will mark ready-for-review once the dependency chain unblocks the temp patch removal.

Copy link
Copy Markdown

@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.

Reviewed commit: df61470c54

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

Comment thread Cargo.toml Outdated
[dependencies]
agent-client-protocol = { version = "=0.11.1", features = ["unstable"] }
# Temporary local override until `session/setTitle` is released in the Rust SDK.
agent-client-protocol = { version = "=0.11.1", path = "../rust-sdk/src/agent-client-protocol", features = ["unstable"] }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace local path override with a resolvable dependency

Avoid committing agent-client-protocol as a local path dependency here, because path = "../rust-sdk/src/agent-client-protocol" requires a sibling checkout that is not part of this repository. In a normal clone (including CI and downstream builds), Cargo cannot resolve that path, so cargo build --locked fails before compilation. This effectively makes the commit non-buildable outside the author’s local workspace.

Useful? React with 👍 / 👎.

@daniel-agentee
Copy link
Copy Markdown
Author

Thanks @chatgpt-codex-connector — addressed in 24ce239.

Replaced the path = "../rust-sdk/..." override with a proper [patch.crates-io] block at the bottom of Cargo.toml. The [dependencies] line for agent-client-protocol is now identical to main (just version = "=0.11.1"); the override lives in a clearly-marked temporary block that cleans up to a single deletion + version bump once the two upstream PRs in the chain release (linked in the PR description).

cargo build --locked and cargo test --locked --lib are now green from a fresh clone.

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

Copy link
Copy Markdown

@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.

Reviewed commit: 24ce239c69

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

Comment thread src/codex_agent.rs Outdated
) -> Result<SetSessionTitleResponse, Error> {
info!("Setting session title for session: {}", args.session_id);

let thread = self.get_thread(&args.session_id)?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Support renaming sessions that are not currently loaded

session/setTitle now fails fast on self.get_thread(&args.session_id), which only resolves sessions in the in-memory self.sessions map. After session/close (or before session/load), a session can still appear in session/list but is no longer in that map, so rename requests for listed historical sessions return resource_not_found instead of persisting the new title. This breaks the main rename flow for non-active sessions; the handler should allow a DB/index-only rename path when no live thread is attached.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

@daniel-agentee daniel-agentee May 28, 2026

Choose a reason for hiding this comment

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

Addressed in 269c0d3.

session/setTitle no longer fails on sessions missing from the live sessions map. The handler now parses the thread id and updates SQLite first, then either delegates to Thread::set_title for loaded sessions or writes the title index directly for historical/listed sessions and emits SessionInfoUpdate from the agent connection. Added set_session_title_persists_unloaded_session to cover that path.

Previously the title was persisted to session_index.jsonl but the
connected client (Zed) only saw the change on the next session/list
refresh — usually after restart. Send a SessionUpdate::SessionInfoUpdate
carrying the new title so the client can update its session list
in place, no restart needed.

Covers both rename surfaces: the session/setTitle JSON-RPC and the
/rename builtin slash command both flow through handle_set_title.
@daniel-agentee daniel-agentee force-pushed the acp-set-title-handler branch from 24ce239 to 2901d1e Compare May 28, 2026 22:13
@daniel-agentee daniel-agentee force-pushed the acp-set-title-handler branch from 2901d1e to 269c0d3 Compare May 28, 2026 22:17
@Kareemkiller
Copy link
Copy Markdown

Kareemkiller commented May 29, 2026 via email

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@daniel-agentee
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@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.

Reviewed commit: 269c0d3e5f

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

Comment thread src/codex_agent.rs
Comment on lines +840 to +845
Ok(false) => {
debug!(
"Thread metadata unavailable before title update for session: {}",
session_id
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject unloaded sessions missing from the index

When update_thread_title returns Ok(false) for an unloaded session, this only logs and then falls through to append_thread_name below, so a session/setTitle for a typo/stale sessionId is reported as successful and emits SessionInfoUpdate even though session/list cannot ever show that session. Since historical sessions are discovered through the state index, this case should return resource_not_found (or otherwise verify the rollout exists) instead of creating an orphan custom-title entry.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 2ebbb14.

For unloaded sessions, session/setTitle now only proceeds if the state update succeeded or the session can be located via the rollout path lookup. Missing typo/stale IDs return resource_not_found before writing session_index.jsonl or emitting SessionInfoUpdate. Added set_session_title_rejects_missing_unloaded_session for the orphan case and adjusted the historical-session test to use a real rollout file.

@daniel-agentee
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@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.

Reviewed commit: 2ebbb140c2

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

Comment thread src/codex_agent.rs Outdated
})?;
notify_client(SessionNotification::new(
session_id,
SessionUpdate::SessionInfoUpdate(SessionInfoUpdate::new().title(title)),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize cleared title updates

When the user clears the title by submitting an empty or whitespace-only string, list_sessions later treats that stored custom title as absent and falls back to the generated first-message title, but this immediate update sends the raw empty/whitespace title. In that case the connected client repaints to a blank title until it refreshes or re-lists sessions; emit the same normalized/fallback/cleared title here (and in the loaded-session notification path) so immediate UI state matches persisted list state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in c03500e.

Blank or whitespace-only session/setTitle updates now notify clients with the same title session/list would show: the generated first-message fallback when available, or null when there is no fallback. The loaded-session path now receives that normalized notification title from the agent before emitting SessionInfoUpdate. Added coverage for both the unloaded fallback case and the loaded-session clear-to-null case.

@daniel-agentee
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@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.

Reviewed commit: c03500e79d

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

Comment thread src/codex_agent.rs Outdated
}

let title = item
let title = match find_thread_name_by_id(&self.config.codex_home, &thread_id).await {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Batch custom title lookups

When session/list returns a page, this awaits find_thread_name_by_id once per item; that helper scans session_index.jsonl from the end for each id. For users with many renamed sessions or a large append-only index, opening the session list now performs up to 25 repeated full-file scans per page, which can make the UI noticeably slower. Collect the page’s thread IDs and use the existing batched lookup instead so the index is read once.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 1db9f3d.

session/list now collects the page thread ids, calls find_thread_names_by_ids once, and then builds each SessionInfo from that batched map with the same first-message fallback when no custom title is present or the index lookup fails. Local validation remains green.

@daniel-agentee
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@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.

Reviewed commit: 1db9f3d62c

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

Comment thread src/codex_agent.rs Outdated
Comment on lines +761 to +764
let title = thread_names
.get(&thread_id)
.cloned()
.or_else(|| first_user_message.as_deref().and_then(format_session_title));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor blank title updates when listing sessions

When a session already has a custom title and the client clears it with an empty/whitespace session/setTitle, this path still prefers the batch thread_names result. I checked codex_rollout::find_thread_names_by_ids in the pinned rust-v0.133.0 dependency: it scans the append-only index forward and skips blank names, so a newer blank entry from append_thread_name does not replace the older non-empty title. As a result, session/list continues returning the stale custom title instead of falling back to the first user message after a clear; this affects both loaded and unloaded sessions that had been renamed before being cleared.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 703dd29.

The session list no longer uses find_thread_names_by_ids, since that helper skips blank entries. It now does a single local pass over session_index.jsonl and records the latest entry per id, preserving blank/whitespace entries as None so a cleared custom title falls back to the generated first-message title instead of reviving an older custom title. Added batched_title_lookup_honors_latest_blank_clear to cover the regression.

@daniel-agentee
Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants