Skip to content

Mirror codex out-of-credits to the channel#77

Merged
rogeriochaves merged 4 commits into
mainfrom
codex-credits-visibility
May 30, 2026
Merged

Mirror codex out-of-credits to the channel#77
rogeriochaves merged 4 commits into
mainfrom
codex-credits-visibility

Conversation

@rogeriochaves
Copy link
Copy Markdown
Contributor

Summary

  • When a codex agent is out of credits, the bridge now posts an explicit warning to the channel instead of leaving it on a bare "Received user message".
  • Detection is from the rollout: a turn that ends with task_complete{last_agent_message: null} whose preceding token_count reports rate_limits.credits.has_credits == false. The message includes the plan, balance, and the top-up URL.
  • Wording is generic ("no output was produced") since formatCodexRolloutLines mirrors any codex agent, not just the PR reviewer.

Why

The codex pr-reviewer hit its usage limit and the channel showed three "Received user message" lines with no follow-up, so there was no way to tell from Slack that it was stuck on credits. The usage-limit text only renders in the codex TUI, never in the rollout, but the credits state is in the structured token_count event, so we can surface it cleanly.

Test plan

  • formatCodexRolloutLines posts an out-of-credits warning when a turn produces no output and credits are exhausted
  • No warning when a turn completes with output (credits present)
  • Existing mirror behavior (prompts, agent messages, exec commands) unchanged
  • Full node --test suite green

When a codex agent runs out of credits, the turn ends with
task_complete{last_agent_message:null} and the preceding token_count
carries rate_limits.credits.has_credits=false. The bridge previously
mirrored only the received prompt, so the channel sat on a bare
"Received user message" with no explanation of why nothing came back.
Detect that state and post an explicit out-of-credits warning (with plan
+ balance and the top-up URL) so the silence is visible.
…resh

Codex was launched with canResume=false, so every reconcile (box reboot,
spot resume, manual restart) started a brand-new codex session and dropped
the agent's accumulated context. Codex mints its own session id, but it can
resume the most recent session for the launch cwd, and the global bypass
flags work when placed before the `resume` subcommand (verified against a
running process). Detect a prior session via the newest rollout under the
cwd and resume with `--no-alt-screen ... resume --last` so paste still
works and the context survives a restart.
The codex mirror posted the received prompt, every agent message, and
every exec command as separate root messages, so the channel filled with
tool-call noise at top level. Post the received prompt as a thread root and
reply with the turn's activity underneath, so each prompt collapses into
one thread. client.post now returns the message ts and accepts a thread_ts.
Threading only worked for Codex because the bridge posts both the received
prompt and the agent's turns there. For Claude the daemon posts the received
prompt (announce) while the bridge posts the assistant turns, so the bridge
had no parent ts and every tool call landed at the channel root. Share the
turn's thread-root ts through a small per-agent file: the announce (Claude)
and the bridge's user post (Codex) record it, and the bridge threads the
assistant turns under it. Unifies both runtimes on one mechanism.
@rogeriochaves rogeriochaves merged commit a4bafec into main May 30, 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