Skip to content

feat(agents): a codex session carries the agent's name - #151

Merged
rogeriochaves merged 1 commit into
mainfrom
feat/codex-session-name
Aug 23, 2026
Merged

feat(agents): a codex session carries the agent's name#151
rogeriochaves merged 1 commit into
mainfrom
feat/codex-session-name

Conversation

@rogeriochaves

Copy link
Copy Markdown
Contributor

What

A claude agent's session reads tech-debt-fixer wherever the session is listed. A codex agent's read nothing: the launcher names claude with claude --name <slug>, and codex takes no name at launch. So every codex agent was an untitled session in its own resume picker, and an unnamed row in the LangWatch sessions screen, where the row for a background agent could not say which agent it was.

Codex does name a thread, through its own /rename. That writes the name into the index codex resume <name> resolves against, which is the same index the LangWatch session harvest reads, so one command labels the session in both places. The launcher now sends it, once, right after it starts the session and before any prompt reaches the agent, so it can never land in the middle of a turn.

Where it lives

Two entries in the runtime table, not a codex branch in the launch path:

  • nameCommand(slug) - the command that names a started session. Codex: /rename <slug>. Claude has none, it was named at launch.
  • readyMarker - what the pane shows once the runtime will accept that command. Codex draws · in the status line under its composer.

Waiting is what keeps this safe

Codex spends a few seconds on its first frame, and asks about directory trust before drawing it at all:

  Do you trust the contents of this directory? ...
› 1. Yes, continue
  2. No, quit

A command typed into either is lost, or answers that question by picking one of its options. So the pane is read until the runtime's status line is the last line on it, and a session that never gets there is left unnamed. An unnamed session costs a label. A command left sitting in the composer would ride out with the agent's first real prompt and cost the turn.

Checked against a real codex started in an untrusted directory: the launch succeeds, the question is untouched, and nothing is typed.

The Enter is its own keystroke

Sent together with the text, codex reads the newline as part of a paste and keeps the whole line in its composer. That is what happened on the first run of this change: the keys went out, the send reported success, and nothing was named.

› /rename tech-debt-fixer          <- typed, never run
  gpt-5.6-sol low · /home/ubuntu/agent-workspaces/tech-debt-fixer

So the Enter goes out 100ms later, on its own, and the result is confirmed rather than assumed: a submitted command is gone from the pane, and one still on screen means the session has no name. A reconcile prints (unnamed) beside such a launch, and stays silent otherwise.

Checked end to end

A real codex, launched through ensureAgentSession itself, in a directory codex already trusts:

action: launched | named: true | waited: 1502 ms
• Session renamed to tech-debt-fixer. To resume this session run codex resume,
  then select tech-debt-fixer (01a02abb-eeb8-79a0-b7d7-781fb46c8d28)

Codex's index then carries it, and the LangWatch CLI reads the same name back with no change on its side:

{"id":"01a02abb-...","thread_name":"tech-debt-fixer","updated_at":"2026-08-22T18:29:15Z"}
NAMES: [["01a02abb-...","tech-debt-fixer"]]

Tests

Eight, covering both scenarios added to specs/sessions/headless-reconcile.feature:

  • waits for the composer, then renames the thread to the slug
  • submits the command with its own keystroke, not alongside the text
  • reports a command that stayed in the composer as unnamed
  • types nothing into the directory-trust question
  • gives up as soon as the session is gone, rather than waiting it out
  • sends nothing for a runtime that was named at launch
  • reads the marker off the last line, so scrollback cannot pass for ready
  • codex names a started session, claude was named at launch

Gate: 332 tests pass, tsc --noEmit clean.

A claude agent's session reads `tech-debt-fixer` everywhere the session is
listed, because the launcher passes `claude --name <slug>`. A codex agent's
read nothing at all: codex takes no name at launch, so every codex agent was
an untitled session in its own resume picker and in LangWatch's sessions
screen, where the row for a background agent could not say which agent it was.

Codex does name a thread, through its own `/rename`. That writes the name into
the index `codex resume <name>` resolves against, which is the same index the
LangWatch session harvest reads, so one command labels the session in both.
The launcher now sends it, once, right after it starts the session and before
any prompt reaches the agent, so it can never land in the middle of a turn.

Two things the runtime table now carries, rather than a codex branch in the
launch path: the command that names a started session, and what the pane shows
once the runtime will accept one. Claude has neither, and nothing is sent for
it.

Waiting for that second one is what keeps this safe. Codex spends a few
seconds on its first frame, and asks about directory trust before drawing it
at all; a command typed into either is lost, or answers a question about trust
by picking one of its options. So the pane is read until the runtime's status
line is the last thing on it, and a session that never gets there is left
unnamed. An unnamed session costs a label. A command left sitting in the
composer would ride out with the agent's first real prompt and cost the turn.

The Enter is its own keystroke, 100ms after the text. Sent together, codex
reads the newline as part of a paste and keeps the whole line in its composer,
which is what happened on the first run of this: the keys went out, `send`
reported success, and nothing was named. So the result is confirmed rather
than assumed, by reading the command back off the pane, and a reconcile prints
`(unnamed)` beside a launch whose name did not take.

Claude-Session: https://claude.ai/code/session_01BUKKUiZbSBHmrLK9JJj4Ba
@rogeriochaves
rogeriochaves merged commit 56a02c0 into main Aug 23, 2026
1 check passed
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.

1 participant