Skip to content

Desktop app-server should coalesce renderer-facing delta notifications to reduce event storms #30430

Open

Description

@code-yeongyu

Summary

Codex Desktop can overwhelm the renderer with very high-frequency, tiny app-server delta notifications during long/tool-heavy turns. On macOS I observed the renderer becoming the hot process while the app-server and MCP bridge processes were still alive and mostly low CPU.

The likely problem is not that every bridge/tool process dies. It is that the renderer has to parse, reconcile, render, and persist too many small event updates.

Local evidence

During the freeze investigation:

  • Codex Desktop was running /Applications/Codex.app/Contents/Resources/codex app-server --analytics-default-enabled and stdio app-server children.
  • Renderer processes were the visible hot path during the UI freeze.
  • The app-server and bridge/helper processes remained alive.
  • ~/.codex/logs_2.sqlite had grown to roughly 952 MB on the affected profile.
  • Local logs/state showed many stream/delta-oriented events around agent text, command output, SSE/websocket/app-server traffic.

I intentionally omitted raw logs and full process command lines because they can contain private paths, local session text, or capability-token file paths.

Concrete suspected bug

The app-server transport currently forwards renderer-facing deltas one notification at a time. In a bursty stream this can mean thousands of JSON-RPC notifications such as:

AgentMessageDelta("h")
AgentMessageDelta("e")
AgentMessageDelta("l")
AgentMessageDelta("l")
AgentMessageDelta("o")

Each notification can cause renderer-side JSON parsing, state reconciliation, transcript updates, and log/state persistence. Long active sessions and tool-heavy runs amplify the cost.

Suggested fix

Coalesce adjacent renderer-facing delta notifications before writing them to the Desktop client:

  • coalesce AgentMessageDelta only when thread_id, turn_id, and item_id all match;
  • coalesce CommandExecutionOutputDelta only when thread_id, turn_id, and item_id all match;
  • preserve order by appending delta strings;
  • flush before non-delta notifications such as item completion;
  • flush when the stream identity changes;
  • do not coalesce messages that carry write-completion acknowledgements;
  • avoid delaying sparse/slow streams: drain only the currently queued burst and flush when the writer queue becomes empty;
  • consider the same invariant for websocket/remote-control transports if they have the same renderer-facing event pressure.

A local prototype in codex-rs/app-server-transport used this shape:

before: 3 queued adjacent deltas -> 3 stdout JSON lines
 after: 3 queued adjacent deltas -> 1 stdout JSON line with concatenated payload

Validation from local prototype

The prototype was not installed into the signed Desktop bundle, but focused Rust tests passed against the transport package:

cargo test -p codex-app-server-transport outgoing_message_coalescer
# 8 passed

cargo test -p codex-app-server-transport transport::stdio
# 3 passed

The tests covered:

  • adjacent agent-message delta coalescing;
  • adjacent command-output delta coalescing;
  • thread/turn/item boundary preservation;
  • flushing before non-delta notifications;
  • preserving write-completion ack behavior;
  • reducing burst stdout writes;
  • flushing sparse deltas when the writer queue drains.

Expected behavior

A burst of renderer-facing text/output deltas should result in fewer client writes/events while preserving the exact user-visible text and event ordering.

Desktop should remain responsive during long streaming turns because the renderer receives a bounded event rate instead of every tiny stream fragment as a separate UI event.

Related issues

This is probably a narrower fix surface under the broader Desktop renderer/state pressure family, related to:

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    app-serverIssues involving app server protocol or interfaces
    on Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions