Skip to content

feat(desktop): opt-in live Working lane for Codex commentary - #66318

Closed
null-runner wants to merge 7 commits into
NousResearch:mainfrom
null-runner:feat/desktop-live-commentary-lane
Closed

null-runner wants to merge 7 commits into
NousResearch:mainfrom
null-runner:feat/desktop-live-commentary-lane

Conversation

@null-runner

Copy link
Copy Markdown
Contributor

Bug

On the Desktop app, the Codex commentary/analysis channel — mid-turn narration like "reading the screenshot first", "checking the logs" — has nowhere to go. Every other surface delivers it via the interim path (messaging gates sending on display.interim_assistant_messages; the CLI prints it), but tui_gateway never sets interim_assistant_callback. With the callback unset, codex_runtime folds commentary into the reasoning channel, so on Desktop it surfaces inside the "Thinking" disclosure instead of as user-facing progress — and during long tool-heavy runs the user watches Thinking/tool rows with no narrative context.

Fix

Wire the desktop to the commentary engine that already exists on main, behind an opt-in setting (default off → zero behavior change):

  1. Backend gatetui_gateway sets interim_assistant_callback (emitting a commentary.delta WS event) only when display.commentary_lane is enabled. When off, the callback stays unset and commentary falls back to the reasoning channel exactly as today. The gate is backend-side on purpose: once the callback is set, codex_runtime routes commentary away from reasoning (on_commentary_message instead of the reasoning fallback), so a renderer-only gate would drop commentary entirely when off. Exposed as the config.get/config.set key commentary_lane.
  2. Renderer — consumes commentary.delta into a dedicated data-commentary message part with its own grey "Working" disclosure, separate from Thinking and from the final answer, flushed in semantic order (reasoning → commentary → answer). It is a data part, not a text part, so completeAssistantMessage's replaceTextPart can't discard it on completion.
  3. Toggle — Settings › Appearance gets a "Live Working Lane" switch, config-backed via commentary_lane.

Scope

  • Default off — no config change means byte-identical behavior to today.
  • Live-only — reload does not reconstruct the lane (commentary is not persisted into reasoning/content when the lane is active), so a reloaded turn shows just the answer. Documented, not a regression.

Relationship to #62396

#62396 proposes a full, end-to-end dedicated commentary lane (backend adapter changes, reload derivation, CLI/messaging recap). This PR is deliberately narrower: an opt-in, desktop-only wiring that reuses the commentary engine already merged on main (_fire_streamed_codex_commentary / interim_assistant_callback), with no changes to codex_runtime or the adapters. If the full lane in #62396 lands, I'm happy to close or rebase this — referencing it here so maintainers can weigh both.

Tests

  • tests/tui_gateway/test_protocol.py — emits commentary.delta when enabled; skips already_streamed/empty; omits the callback entirely when the lane is off (upstream-identical).
  • commentary.test.tsxcommentary.delta → commentary part (never reasoning); reasoning → commentary → tool → commentary → answer ordering; commentary stays above the answer in a single flush window; consecutive deltas coalesce.
  • streaming.test.tsx — renders a separate "Working" disclosure, not under Thinking.
  • commentary-lane.test.ts — the config-backed store round-trips commentary_lane.
  • tsc (renderer + electron) clean; vitest --project electron unaffected.

Checklist

  • Conventional Commit
  • Opt-in, default off — no behavior change unless enabled
  • Only changes related to this feature
  • Cross-platform impact considered (no new platform assumptions)

On the Desktop app the Codex commentary/analysis channel (mid-turn narration)
has nowhere to go: tui_gateway never sets interim_assistant_callback, so
codex_runtime folds commentary into the reasoning channel — it shows up inside
the "Thinking" disclosure rather than as user-facing progress. Every other
surface (messaging, CLI) already delivers commentary via the interim path.

This wires the desktop to that same engine behind an opt-in setting:

- tui_gateway sets interim_assistant_callback (emitting a commentary.delta WS
  event) only when display.commentary_lane is enabled. Default off, so nothing
  changes unless you turn it on; when off the callback stays unset and
  commentary falls back to reasoning exactly as before. The gate is backend-
  side on purpose: once the callback is set codex_runtime routes commentary
  away from reasoning, so a renderer-only gate would drop it entirely.
- The renderer consumes commentary.delta into a dedicated data-commentary part
  with its own grey "Working" disclosure, separate from Thinking and from the
  final answer, flushed in semantic order (reasoning -> commentary -> answer).
  It is a data part, not a text part, so completion's replaceTextPart can't
  discard it.
- Settings > Appearance gets a "Live Working Lane" toggle, config-backed via
  the config.get/config.set key `commentary_lane`.

Live-only: reload does not reconstruct the lane (commentary is not persisted
into reasoning/content), so a reloaded turn shows just the answer.

Tests: tui_gateway commentary event + gate; renderer commentary lane (event ->
part, ordering, coalescing) plus a render case (Working vs Thinking); the store.

Relates to NousResearch#62396, which proposes a full end-to-end dedicated display lane;
this is a narrower, opt-in, desktop-only wiring that reuses the commentary
engine already on main. If the full lane lands, happy to close or rebase this.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) provider/openai OpenAI / Codex Responses API area/config Config system, migrations, profiles sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades needs-decision Awaiting maintainer decision before any implementation labels Jul 17, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Related: #62396 implements a broader end-to-end dedicated commentary lane, while this PR is an opt-in Desktop/TUI-gateway design. The overlapping user-facing goal needs a maintainer choice rather than a duplicate closure.

commentaryPart built {type:'data-commentary'}, but assistant-ui routes data
parts to components.data.by_name by the part's `name`, not by a `data-<name>`
type — so the commentary part never matched the registry and the "Working"
lane rendered nothing even though commentary.delta was delivered.

Build the part as {type:'data', name:'commentary', data} and match it by name
in the stream coalescer. The render tests now build the fixture from the real
commentaryPart() factory instead of a hand-written shape, so a shape drift can
no longer pass while the runtime renders nothing.
Two small presentation tweaks for the opt-in Working lane:
- It now rests OPEN by default (a new `defaultOpen` on the shared disclosure,
  set only for the commentary slot). The lane is opt-in, so once enabled the
  narration should be visible immediately rather than collapsed — a completed
  turn was showing an empty "Working" header until clicked open. The user's
  explicit toggle still wins.
- Panel restyled from flat grey to a soft grey-blue with a thin left accent so
  it reads as its own surface without shouting; rgba tints blend over both
  light and dark themes.

The render test now asserts the narration text is visible with no interaction.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Clean, well-scoped change with good test coverage. No security concerns.


Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the narrowly scoped Desktop implementation. The current-main premise is valid: tui_gateway/server.py:4155 supplies no interim callback, while agent/codex_runtime.py:1032-1040 routes commentary to reasoning when no dedicated callback is supplied.

Problems

  • Proposed tui_gateway/server.py:4230 samples the config only when _agent_cbs() builds an agent. Proposed config.set at :10862-10865 only persists YAML; current-main agent construction happens earlier via **_agent_cbs(sid) at tui_gateway/server.py:4969. The Settings control therefore cannot change an existing session's callback.
  • commentary.delta is a shared tui_gateway event, but the Ink TUI has neither a union entry in ui-tui/src/gatewayTypes.ts:617-716 nor a handler alongside its reasoning.delta and message.delta cases in ui-tui/src/app/createGatewayEventHandler.ts:679-681,945-948. Enabling this profile setting would hide commentary on that surface rather than preserve its current reasoning fallback.

Suggested changes

  • Apply the callback change to live agents or make next-session activation explicit and tested.
  • Gate the event by Desktop capability, or preserve the Ink TUI fallback and cover it.

This is an automated hermes-sweeper review.

Comment thread tui_gateway/server.py
# already_streamed means the text is the answer already relayed via
# message.delta — skip it. Each call is one completed commentary message; the
# trailing blank line separates messages that flush in the same window.
if _load_commentary_lane():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

config.set commentary_lane only writes configuration; it does not mutate agents that were already constructed with _agent_cbs(). Enabling or disabling this control in an existing Desktop chat therefore has no effect on that chat. Update the live session callback here/from the setter, or make next-session activation explicit and cover both transitions.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 18, 2026
The display.commentary_lane setting was only sampled inside _agent_cbs,
which runs once when _make_agent builds the agent. Flipping the toggle
therefore only affected sessions created afterwards; running sessions
kept whatever state they were born with.

codex_runtime re-reads agent.interim_assistant_callback on every LLM
call, so propagating the toggle is just a matter of flipping that
attribute on the live agents. config.set now calls
_apply_commentary_lane_to_live_sessions, which wires the commentary
callback when enabling and clears it (back to None) when disabling so
the off path stays byte-identical to upstream, with commentary falling
back onto the reasoning channel. The opt-in default is unchanged.
commentary.delta is emitted by the shared tui_gateway when the desktop's
opt-in Working lane is on, but ui-tui had no entry for it in the
GatewayEvent union nor a handler, so a TUI client attached to a gateway
with the lane enabled silently dropped every commentary message.

Add the event to the type union and route it into the reasoning channel,
which is exactly where upstream puts Codex commentary when no lane
callback is installed (see agent/codex_runtime's on_reasoning_delta
fallback). This keeps the TUI behaviour equivalent to upstream instead
of dropping the event, and empty payloads are ignored without throwing.
…mentary-lane

# Conflicts:
#	tui_gateway/server.py
…ages

Upstream merged display.interim_assistant_messages (default on), which also
installs interim_assistant_callback. The lane-disabled test assumed the
callback was absent whenever commentary_lane was off; disable both settings so
it tests only the commentary lane's own contribution.
@null-runner

Copy link
Copy Markdown
Contributor Author

Closing this one: we ended up preferring the current upstream inline-commentary behavior in daily use, so the opt-in lane no longer earns its complexity. #62396 also covers a broader take on the same surface. Thanks for the triage pointers.

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

Labels

area/config Config system, migrations, profiles comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have provider/openai OpenAI / Codex Responses API sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants