Adopt externally managed zmx sessions#604
Conversation
|
Thanks, this now meets the contribution policy. I've cleared the |
4f2aa8d to
7a47eea
Compare
7a47eea to
583857a
Compare
Quirk: an adopted zmx session running a mouse-tracking TUI (Claude Code) can't scroll until the first keystrokeHit this dogfooding Symptom. A tab adopted onto a live zmx session running Claude can't be scrolled (wheel/trackpad) — it looks frozen. Any keystroke, or a window resize, instantly fixes it. Only Claude is affected (codex/gpt don't rely on SGR mouse events). I confirmed it's not on supacode / libghostty side: ghostty keeps Why. supacode attaches as a non-leader client. In // util.zig — isUserInput()
// mouse events: CSI M / CSI < — EXCLUDE these
// only intentional keyboard input should trigger leader switch
if (csi.final == 'M' or csi.final == '<') return false;// main.zig — Daemon.handleInput()
if (leader == client) { queuePtyInput(payload); return; } // leader: forward all
if (isUserInput(payload)) { // non-leader: only "user input"
try setLeader(client); // → promotes to leader
queuePtyInput(payload); // → and forwards
}
// a non-leader's mouse: neither promotes nor forwards → silently droppedSo the wheel events are dropped. A keystroke passes Both intents behind this look right to me: leader = resize authority, and ambient mouse motion/focus shouldn't steal the leader from whoever's typing. The quirk is that |
|
@sbertix This PR was ready for review now, 2-days local dogfooding works fine with it's local build version and my local link-bridge launchd scripts.
Thks for your efforts. Any following up and revision suggestions are welcome. |
|
The main pain points aren’t really the issues or suggestions themselves (and there are several, since we’re here, e.g., the I held back from giving more structured feedback before because I’m worried about the direction. The core |
@sbertix Thanks for the detailed read — and agreed the worthy worries. I only use supacode to serve my use case and didn't hold the critical big picture, happy to keep it out of core until/unless the shape feels right. No pressure either way. And if I understand the situation correctly, the scroll quirk above plus the failure modes you listed are symptoms of the same thing: the primitive has supacode display and act on a session it doesn't own, so it inherits a large, leaky surface (lifecycle, kill-safety, ID identity, even the multiplexer's input model) it can't fully control. If there is any appetite, I'd rather find the smallest contract everyone would be comfortable owning than push the current surface. A maximally narrowed version in my mind might be:
But still, I don't know supacode's full workflow surface or codebase well enough to judgee, and don't obtain rich variety of workflow variants to dogfooding with supacode. So I'll gladly defer to whichever direction you prefer — including shelving this. |
|
I'm worried about the opposite, tbh: that the contract is not "big enough" to actually treat attached sessions as fully fledged Supacode ones. We're introducing a second-class surface, without indication, without further escalation possibilities. |
|
Understood. I'll keep running it locally. Whenever the shape feels right to you, I'm happy to reshape or redo (or some other PR) this to match. |
Closes #591
Summary
Adds a
supacode tab adopt-zmx <session>command (and a matching…/tab/adopt-zmxdeeplink) so an external orchestrator can surface a zmx session it already manages as a Supacode tab. Supacode only attaches — it never creates or kills the external session. Re-running is idempotent: the tab id is derived from the session name, an unchanged sync neither steals focus nor churns the sidebar, and closing or pruning an adopted tab spares the external session (only Supacode-owned sessions are torn down). Adopted tabs are excluded from the layout snapshot, so a restart defers re-adoption to the owner instead of restoring a detached shell.Type of change
ready)How was this tested?
make checkandmake testpass locally, and I built, ran, and dogfooded the app.Dogfooding used a small external orchestrator (a
launchdagent) that syncs live zmx sessions from a remote devbox into Supacode. Simplified model:The stable UUID pins each remote session to one tab across polls; the manifest digest makes an unchanged sync a true no-op. Running this every few seconds against real devbox sessions is what surfaced the focus-steal, sidebar-churn, and external-session-kill cases fixed here.
make checkpasses (format + lint)make testpassesAI tool disclosure (optional)
Checklist
Closes #above.ready.