Skip to content

(remote): light the attention state when a remote session waits on a dialog (#394) - #395

Merged
devsuitup merged 4 commits into
mainfrom
feat/394-remote-attention
Oct 2, 2026
Merged

devsuitup merged 4 commits into
mainfrom
feat/394-remote-attention

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

What

An unattached remote session whose live descriptor says status: "waiting" now shows the orange attention state, and loses it when the status leaves waiting, the descriptor disappears or the session is dead. The sidebar status line appends the descriptor's waitingFor (waiting · permission prompt · 3m ago). Attached rows are unchanged (#273).

Why

Under tmux on a host the CLI emits no OSC 9, so a remote session waiting on a dialog stayed dark. The descriptor already reaches the renderer every refresh; waiting means a blocking dialog is open (read from the CLI bundle 2.1.286). Latency is the refresh cycle (about 15 s with the host watch).

How

  • public/session-state.js: descriptorStatus accepts attention: true. waiting then sets attention (clearing busy/unread like an attention event) and records that the descriptor owns it; any other status, liveness: dead or an explicit attention event releases it. Attention raised by an explicit event is never cleared by a descriptor. Busy edges and transcript touches never clear it. Only the remote adapter passes the flag, so local rows are unchanged.
  • remote-index.js: keeps waitingFor on the descriptors and placeholders, validated (string, trimmed, at most 64 characters, no control characters, otherwise dropped). main.js copies it onto the session object; public/sidebar.js shows it while the status is waiting.
  • Docs: .ai/contexts/session-state.md (rule), .ai/contexts/cli-session-state.md (reservation updated), docs/remote-hosts.md, CHANGELOG.

Tests

test/session-state.test.js (9 reducer tests), test/remote-session-adapter.test.js (unattached lights and clears, attached not painted, decay keeps attention), test/remote-index.test.js (sanitizer, indexer, placeholder), test/annotate-remote-attachable-local-status.test.js, test/dom-sidebar-local-status.test.js. Each was red before the change. Fourteen single-line mutations of the guards (flag requirement, release paths, dead-liveness guard, ownership takeover, clearExclusive, length bound, control-character check, type check, placeholder and indexer copy, main.js copy, sidebar condition) each turn a test red.

Not verified

A live descriptor during a permission prompt on a declared host; OSC 9 under tmux, the injection channel (#219) and the tier ladder (#218) are out of scope.

Closes #394

…dialog

Under tmux on a host the CLI emits no OSC 9, so an unattached remote
session waiting on a permission prompt or a question stayed dark. The
descriptor already carries status "waiting" (a blocking dialog is open);
the remote adapter now turns it into attention, released when the status
leaves waiting, the descriptor is gone or the session is dead. waitingFor
is kept, validated, and shown in the status line.

Closes #394
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing 48b6f23 (adversarial review in progress).

@devsuitup

Copy link
Copy Markdown
Owner Author

Adversarial review at 48b6f23: no blocker, no major; two minors being fixed in a follow-up commit.

  • Detach after answering (public/remote-activity-ui.js setRemoteAttached): the reducer keeps taking descriptors while attached, and the detach handoff only clears busy, so attach → answer in the PTY → detach before the next pull projects a stale orange until that pull. Verified by reading the handoff.
  • Host outage: a failed refresh keeps the last descriptors, so a host that drops mid-dialog keeps the row orange for the whole outage. Attention will be gated on a fresh host.
  • Nit: waitingFor will also reject bidi and zero-width controls (the only sink is textContent, no XSS).

Checked and holding: a local row never lights from its own descriptor (only the remote adapter passes attention: true); status idle/busy, descriptor gone, pid dead and stop all release it; clearNotifications touches only local-pty states, so it does not fight the descriptor; the tests drive the production modules. 88 tests pass on the touched files.

The handoff to a detached row projected a frozen attention left by the
descriptor, and a host that dropped mid-dialog kept the row orange for the
whole outage. Detach now releases descriptor-owned attention and a host in
error passes attention:false with its descriptor. Rebuilt rows read the
remote state at initial paint so the render no longer wipes the class, and
waitingFor rejects bidi and zero-width controls.

Refs #394
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing 15d3bb3 (adversarial review in progress).

… filter

A host that started failing or recovered with no file changes triggered
no re-render, so the host-error gate on attention waited for an unrelated
render. The indexer now notifies when a host's last error changes, once
per change. waitingFor also rejects C1 controls, soft hyphen, ALM, line
and paragraph separators, invisible operators and the BOM.

Refs #394
sidebar.js called writeIconSlot, which is not a declared cross-file global
and which only session-activity-dom.js may call. Add
paintSessionIconFromSnapshot there, declare it in eslint.config.js and use
it from the sidebar.

Refs #394
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing 4ba4267 (adversarial review in progress).

@devsuitup

Copy link
Copy Markdown
Owner Author

Re-review at 4ba4267: no blocker, no major. Notify fires on the first failure, a different error and recovery — not on a repeated identical error (tests drive refreshNow/refreshHostNow with an injected notify). paintSessionIconFromSnapshot keeps session-activity-dom.js the only writer of the icon slot, and the sidebar calls it only for an unattached remote state with attention. Sanitizer keeps no-break space and accented text. Minor, accepted: an ssh stderr that varies per cycle would notify each failing cycle (bounded by the backoff). CI green on 4ba4267 (lint, changelog, test 20/22 × ubuntu/windows, builds). Ready to merge.

@devsuitup
devsuitup merged commit cd78b0b into main Oct 2, 2026
11 checks passed
@devsuitup
devsuitup deleted the feat/394-remote-attention branch October 2, 2026 09:49
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.

(remote): light the attention state when a remote session waits on a dialog

1 participant