v0.76.0 — the session page can answer an engine
The session page can answer an engine
v0.75.0 made a command's output visible on app.moshcode.sh/sessions/<id>. This release fixes the other direction, and the case that made the gap obvious: run /agents claude in a folder Claude has not seen before, and the page shows you "Do you trust the files in this folder?" sitting on No, with an arrow pad that cannot move it.
The keys were being delivered. They were delivered to the wrong place.
pressKey ended in stdin.emit("data", "�[B") — a synthesised event on the moshcode process's own stdin object. Readline hears that, and so does anything else reading in-process: the herd bar, the reader, a menu. A child spawned with stdio: "inherit" reads a file descriptor, and hears nothing at all. By the time an engine is up, the pit's readline has been closed for the hand-off, so ↓ arrived and went nowhere.
Everything else was already right — the D-pad, the five-key allow-list, the features: ["keys"] capability negotiation, the pty output capture. What was missing was a file descriptor between the browser and the program.
So moshcode now owns the engine's stdin. captureSpec grows an input option that puts a fifo there instead of the tty, which script(1) reads and copies to the pty master exactly as it would a terminal — no native module, nothing outside the base system, which is the same reason script was chosen over node-pty in the first place. The herd has used this substrate for detached sessions all along; the engine hand-off simply never got it.
Owning stdin costs two things back, and both are paid rather than written off as limitations:
scripttakes the pty's geometry from its own stdin, and a fifo has none — the child would start on a 0x0 terminal, which no full-screen engine survives. Nothing outside a pty can ioctl its master, butsttyinside it can, so the session sizes itself on the way in.scriptcan no longer forwardSIGWINCH, for the same reason. The child records its pty path as it starts, which is enough to resize it from outside, so dragging a window edge still reaches the engine. The herd gives up here; this does not have to.
The person at the keyboard keeps working throughout. Local stdin is relayed byte-for-byte into the same fifo, in raw mode — the pty on the far end is now the one echoing and splitting lines — and the terminal is handed back exactly as it was found when the engine exits.
Cursor keys in the form the program asked for
A bug found only by testing against something full-screen, and worth spelling out because it is invisible until you synthesise keys.
A TUI usually sets DECCKM (ESC [ ? 1 h) on its way in, after which it wants the SS3 form of the cursor keys — ESC O B for down, not ESC [ B. A real terminal switches forms silently when asked, which is why nobody meets this at a keyboard. Fed the CSI form in that mode, less does not scroll: it prints ESC[B on its own prompt line, as though you had typed the characters.
The mode cannot be queried, but it is announced — and the child's output is already passing under our nose on its way to the mirror. moshcode reads DECCKM off that stream and sends whichever form is current. A stand-in child that echoes its bytes passed this the whole time; only a real full-screen program caught it.
Typed lines follow the keys
With an engine up, a line sent from the page now goes to the engine rather than parking in a queue for a prompt that will not come back until it exits. Without that you could answer a menu and not a question, which is half a session page.
Blast radius
Nothing changes for a pit that is not mirroring, a piped or scripted run, or a box with no script(1) we can drive — the plain inherited launch runs exactly as before, and MOSHCODE_MIRROR_PTY=0 still forces the whole thing off. Only the engine hand-off opts in; /install, !cmd and the other launchers keep the output-only capture they gained in v0.75.0.
Verified against the real claude binary in a fresh untrusted directory: one key sent from the page moves the selector from ❯ No, exit to ❯ Yes, I trust this folder.
Upgrading
moshcode upgrade, or npm i -g moshcode@0.76.0.