feat(agents): a codex session carries the agent's name - #151
Merged
Conversation
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
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.
What
A claude agent's session reads
tech-debt-fixerwherever the session is listed. A codex agent's read nothing: the launcher names claude withclaude --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 indexcodex 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:
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.
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
ensureAgentSessionitself, in a directory codex already trusts:Codex's index then carries it, and the LangWatch CLI reads the same name back with no change on its side:
Tests
Eight, covering both scenarios added to
specs/sessions/headless-reconcile.feature:Gate: 332 tests pass,
tsc --noEmitclean.