Skip to content

Refresh Cloud transcript terminal bindings when reopening chat - #16069

Merged
teamleaderleo merged 4 commits into
fix/cloud-terminal-rpc-lifetimefrom
fix/cloud-transcript-terminal-rebind
Oct 1, 2026
Merged

teamleaderleo merged 4 commits into
fix/cloud-terminal-rpc-lifetimefrom
fix/cloud-transcript-terminal-rebind

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

When a terminal agent is resumed on a different pane with the same transcript file, reopening its chat reuses the existing session without updating its terminal binding. Focus Terminal then activates the old pane. Refresh changed bindings when reusing a transcript, while preserving its reader, history, and subscribers. Removing a surface binding also removes the stale focus destination.

Replacing only changed bindings fences failures from the old terminal without dropping valid replies during repeated resolution of an unchanged binding.

Depends on #16068 and targets fix/cloud-terminal-rpc-lifetime so the diff contains only this fix.

Validation

  • Regression commit: 37aaac55fb1875c80181c7f99c9ccf681a72d785.
  • Production fix: 27fea8add110a54617e4241f22245562b5b997be; verified head: 007226e4bfac0c8d63225f854550c5af6e5203a5 (test assertion typing correction).
  • From agent-chat, bun test/transcript-terminal-rebind.test.ts: failed before the fix because Focus Terminal used old-terminal; passed after it. The final fixture was also rerun against the regression commit's server and the verified head with the same result.
  • The fixture calls the session reuse path used by terminal chat resolution, then invokes the real transcript focus action with an isolated RPC capture. Covers Claude and Codex, unchanged transcript history/attachment, changed and removed surfaces, old replies after rebinding, and current failures after repeated unchanged resolution. It does not send commands to a running cmux instance.
  • bun run check: passed (typecheck, 27 scripts, 44 tests across 13 bun:test files).
  • Production browser bundles for src/main.tsx and src/gallery-main.tsx: passed.
  • git diff --check 25677e569383a99a9fd3a898328b19b85b069797..HEAD: passed.

No live Cloud instance or browser validation.

Changelog

Fixed: Reopening an agent transcript after it moves to another terminal updates Focus Terminal without resetting chat history.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Fixes reopening a terminal agent's transcript after it moves to another pane so Focus Terminal activates the current terminal instead of the old one.

  • Refreshes changed transcript terminal bindings when a session is reused, preserving its reader, history, and subscribers.
  • Removing a surface binding also clears the stale focus destination.
  • Replaces only changed bindings so old RPC failures are fenced without dropping valid replies from unchanged bindings.
  • Adds a regression test covering Claude and Codex, changed and removed surfaces, and stale replies after rebinding.

Written for commit b78c803. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: fc9408c1-5839-4bd3-9faa-929e75a384e2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent, parent→head; merge base is 8e71daa1441, main is 77 ahead).

This is the one that fixes a user-visible bug, and it merges into current main cleanly and semantically correctly: I checked the merged tree and main's resetSessionHistory branch and attention seeding both survive with the rebind block added. Load-bearing: reverting only the behavioural hunk (keeping the test export so the failure is an assertion, not a missing-export SyntaxError) fails with actual: "old-terminal", expected: "new-terminal".

Worth calling out as a strength: the test covers the no-false-invalidation case ("Repeated resolution with identical IDs must not discard a valid reply"). That is the single most likely thing to get wrong here, and it is what keeps this PR from breaking #16068. If the target object were replaced on every subscribe, #16068's identity guard would start rejecting each request's own legitimate failure, which reads as "errors stopped appearing" and nothing would fail.

should-fix (dead code) — server.ts:649, the target?.agentSessionId !== source.sessionId clause is unreachable. existing comes from sessions.get("t-" + source.sessionId), and all three writers of internal.transcriptTarget (server.ts:652, :663, :673) set agentSessionId = source.sessionId for the same source that keyed the lookup; resolveSessionTranscript only returns a source whose sessionId equals the id it was asked for (transcript-sources.ts:197). Proven by replacing the clause with a throw: this PR's test and all 28 scripts stayed green. Only the surfaceId half does any work, so reduce the condition to that (keeping !target if you want the defensive guard) and it will say what it means.

nit — server.ts:687: startTranscriptTail's onTick: () => refreshTranscriptAttention(sess, source) captures the pre-rebind source, so after a rebind the closure holds a stale surfaceId. Benign now because refreshTranscriptAttention reads only source.agent and source.sessionId, both unchanged; a live bug the moment attention keys on the surface.

Order: after #16068, which it depends on for its fencing assertions (on main focusTranscriptTerminal never calls sess.emit, so sess.events.length === 2 cannot pass).

tsc exit 0, 44 pass / 0 fail, 27 scripts OK.

One premise taken on trust: that the hook store's surfaceId actually changes when an agent resumes in a different pane. The consuming path is verified; the app behaviour that produces it is not.

— Raindrop g2 🫧
Run: run_worker_20260930_3fc64ba6

… into HEAD

# Conflicts:
#	agent-chat/server.ts
@cursor

cursor Bot commented Oct 1, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo
teamleaderleo merged commit b80327e into fix/cloud-terminal-rpc-lifetime Oct 1, 2026
48 checks passed
@teamleaderleo
teamleaderleo deleted the fix/cloud-transcript-terminal-rebind branch October 1, 2026 22:24
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