feat(acp): title Codex sessions from the agent and channel name#3028
Open
wpfleger96 wants to merge 2 commits into
Open
feat(acp): title Codex sessions from the agent and channel name#3028wpfleger96 wants to merge 2 commits into
wpfleger96 wants to merge 2 commits into
Conversation
Codex sessions all showed up unnamed in the thread list, and one agent active in several channels produced several indistinguishable rows because sessions are keyed per channel. The title travels out of band in `session/new` `_meta.sessionTitle`, so it costs no tokens and cannot perturb the prompt contract. `_meta` is omitted entirely when there is no title, since an adapter may distinguish an absent member from a null one. Sanitization and `Agent · #channel` composition live in buzz-acp: the desktop spawn path only resolves `display_name`-or-`name` and exports `BUZZ_ACP_SESSION_TITLE`. Only the channel part is truncated to the 80-char cap, so the agent name always survives. DMs, unresolved channels, and heartbeat sessions keep the bare agent name. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Round-1 review found four correctness defects in the session-title path. An interior NUL in a display name cannot cross the environment boundary at all, so `Command::env` failed the whole spawn — the child-side sanitizer ran far too late. Control characters are now stripped at the desktop boundary, with a fallback to the unique handle when a display name is nothing else. `spawn_config_hash` did not cover the title, so an agent rename left the running process silently stale with no restart badge. It now hashes the same resolve the spawn writes, and skips it when an explicit `BUZZ_ACP_SESSION_TITLE` override shadows the record (the override already reaches the hash through the effective env). The channel lookup ran twice on a new session. `ChannelInfoResolver` caches only `Some`, so an unresolvable channel paid the full `fetch_channel_info` retry sequence twice — ~6.5s each — in front of `session/new`, exactly when the relay is degraded. One resolve now serves both the canvas DM check and the title. `fetch_channel_info` substitutes a literal `"unknown"` name for a metadata event with no `name` tag; that sailed past the DM filter and titled every unnamed channel `Agent · #unknown` — the same collision the suffix exists to remove. It is now treated as absent. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This was referenced Jul 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Codex names an ACP session from the first text it receives. Every managed Buzz agent opens with the same
[Base] You are operating inside the Buzz platform…framing, so the Codex session list shows a wall of identical rows, and because sessions are keyed per channel, one agent active in several channels produces several indistinguishable ones.This sends the name out of band instead.
session/newcarries_meta.sessionTitlewithAgent · #channel, composed from the agent'sdisplay_name(or its uniquenamehandle) and the channel it is serving. The prompt is untouched — no tokens spent, no perturbation of the prompt contract, and nothing new for the desktop observer's section parsing to handle.This half is inert until the adapter ships
ACP adapters ignore
_metamembers they do not recognize, so against today's@agentclientprotocol/codex-acp1.1.x a Buzz session gets no title and nothing else changes. The consuming side is codex-acp#338, which reads_meta.sessionTitleand names the Codex thread from it; neither half is useful alone.codex_adapter_is_outdated_with_pathgates on major version< 1(desktop/src-tauri/src/managed_agents/discovery.rs:1166-1176), and this feature needs no version floor above that, so none is added — an older adapter is not broken by the extra member, it simply ignores it.What changes
crates/buzz-acpowns sanitization and composition.sanitize_session_titlecollapses whitespace, drops control characters, and caps atSESSION_TITLE_MAX_CHARS(80) by character, not byte, so a multi-byte character cannot be split.compose_session_titletruncates only the channel part against that cap, so the agent name always survives; when the agent name alone fills the cap the channel is dropped rather than the name.session_new_fullsets_meta.sessionTitlewhen a title exists and omits_metaentirely when it does not, since an adapter may distinguish an absent member from a null one.desktop/src-taurionly resolves and exports.resolve_session_titlepicksdisplay_nameor falls back toname, andspawn_agent_childwrites it toBUZZ_ACP_SESSION_TITLE— or removes the variable when neither candidate yields anything printable.DMs, unresolved channels, and heartbeat sessions get the bare agent name with no channel suffix.
Four properties that are easy to remove by accident
Control characters are stripped at the desktop boundary, not in the harness. An interior NUL cannot cross the environment boundary at all —
Command::envfails the entire spawn rather than passing it through. Deferring the strip tobuzz-acpwould let a corrupted display name turn display chrome into a spawn failure. A display name that is only control characters falls back toname.The title is hashed into
spawn_config_hash. Without it, renaming an agent left the running process with a stale title and no restart badge. The hash runs the sameresolve_session_titlethe spawn writes, and skips it when a user env override shadowsBUZZ_ACP_SESSION_TITLE— spawn writes the title before the user env layer, so the override is what actually runs, and it already reaches the hash through the effective env. Hashing the record-derived value under an override would badge a rename that changes nothing.One channel resolve serves both consumers.
resolve_new_session_channel_contextreturns(is_dm, title_channel)from a single metadata lookup, feeding both the canvas block's DM check and the title.ChannelInfoResolvercaches onlySome, so two independent calls against an unresolvable channel pay the fullfetch_channel_inforetry sequence twice — two timeouts plus a retry delay each — directly in front ofsession/new, precisely when the relay is already degraded.The
"unknown"channel name is treated as absent.fetch_channel_infosubstitutes the literal"unknown"for a metadata event with nonametag. Composing that sentinel would title every unnamed channelAgent · #unknown, reintroducing the exact collision the suffix exists to remove while naming a channel something it isn't. The startup cache already refuseschannel_type == "unknown"for the same reason.Closes #2334
Related — the adapter halves that consume
_meta.sessionTitle: