Move PTY buffer out of zustand; drop input queueing#31
Conversation
Follow-up to #29 and #30 for the "sticky when typing fast" report. The remaining cost on the hot path was that every PTY chunk rebuilt the agents array in zustand, which woke every component reading `useAgentStore((s) => s.agents)`. Even with the terminal's narrow selector, components like ProjectSidebar, TerminalPane, and GraphView were re-rendering 100+ times/sec for the echo of the user's own keys. Renderer: - New pty-buffer-store: per-agent string[] buffer with key-scoped pub/sub, sitting outside zustand. Terminal and AgentNode subscribe directly by agent key. - agent-store: drop the `ptyBuffer` field from the Agent interface. worker_stream now appends to the pty-buffer-store and only touches zustand state when the agent's activity actually needs to flip from idle → active (which usually means: not at all during a typing session). agent_exited/agent_released clears the per-agent buffer. - use-terminal: subscribe by key instead of selecting from the agents array. - AgentNode: read preview chunks via a small useEffect-based hook that subscribes to the buffer for that node only. Main: - broker: replace queueInput / scheduleInputFlush / flushQueuedInput with a single fire-and-forget sendInput. We don't need backpressure for human typing, and the await on stream.send was adding round-trip latency per byte. Errors are logged; the existing stream-fallback path still reopens broken streams. - ipc-handlers: route broker:send-input-fast to the new method. https://claude.ai/code/session_01BFwPP9s4vXkgsPDtBByLS9
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughBroker input switches from queued/coalesced delivery to a fire-and-forget sendInput API. A new per-agent PTY buffer store (with get/append/clear/subscribe) replaces in-state ptyBuffer, agent state is updated to use it, and terminal hooks/components consume PTY chunks via subscriptions. ChangesInput and PTY Buffer Refactoring
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b41cee8cc6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| existing.push(chunk) | ||
| next = existing |
There was a problem hiding this comment.
Emit new PTY buffer arrays on every append
appendPtyChunk mutates the existing chunk array in place (existing.push) and notifies listeners with the same reference. Subscribers like AgentNode store these chunks in React state via setChunks(next), and React skips re-render when the reference is unchanged, so terminal preview tiles can stop updating after the first chunk (until a trim path creates a new array). Return a fresh array for each append before notifying listeners to keep keyed subscribers reactive.
Useful? React with 👍 / 👎.
In-place existing.push(chunk) handed listeners the same array reference, so React subscribers (AgentNode's setChunks) bailed on Object.is and the graph preview tile would freeze after the first chunk until a trim path happened to allocate. https://claude.ai/code/session_01BFwPP9s4vXkgsPDtBByLS9
Follow-up to #29 / #30 for the "still feels sticky when typing fast" report. Skips local echo per your direction; does the other three.
What changes
Renderer — get the PTY buffer out of zustand.
pty-buffer-store.ts: per-agentstring[]buffer with key-scoped pub/sub, kept outside zustand entirely. Subscribers register against a single agent key.agent-store: drops theptyBufferfield from theAgentinterface.worker_streamnow appends to the pty-buffer-store and only touches zustand state when the agent's activity actually needs to flip (idle → active). During a steady typing session activity is alreadyactive/working, so the agents array isn't rewritten at all per chunk.agent_exited/agent_releasedclears the per-agent buffer.use-terminal: subscribes by key (subscribePtyBuffer) instead of selecting from the agents array.AgentNode: reads preview chunks via a tinyuseEffect-based hook so the graph preview re-renders only for the agent producing chunks, not for every node.Net effect: ProjectSidebar, TerminalPane, GraphView, etc. stop re-rendering on every PTY chunk.
Main — drop input queueing, fire-and-forget input.
queueInput/scheduleInputFlush/flushQueuedInput/inputQueueswith a singlesendInputFireAndForget. We don't need backpressure for human keystrokes, andawait stream.send(data)per byte was adding round-trip latency that shows up as stickiness when typing fast. Errors are logged; the existing stream-fallback path insendInputstill reopens broken streams.ipc-handlers: routebroker:send-input-fastto the new method.Test plan
npm test— green.npm run build— clean.tsc -p tsconfig.web.json --noEmitandtsc -p tsconfig.node.json --noEmit— no errors in touched files.getAgentBuffer).https://claude.ai/code/session_01BFwPP9s4vXkgsPDtBByLS9
Generated by Claude Code