Problem
A provider adapter can establish fresh idle authority and verify its own empty-composer safety rule, but an ordinary input, child output, resize, or daemon replacement can race between that check and delivery. PTY must not guess what Enter, newline, paste, ESC, or any other bytes mean.
Depends on #129.
Proposed generic seam
Expose top-level daemon generation plus a monotonic per-generation ioRevision through live STATUS. Add one bounded conditional write request and public API:
compareAndSend(name, {
generation,
ioRevision,
data,
}): Promise<{
ok: boolean;
generation: string;
ioRevision: number;
error?: string;
}>
The daemon increments revision for every accepted ordinary or guarded input, every child-output chunk, and every actual PTY size mutation. Attach, peek, and status without input or resizing do not increment it.
Guard validation and the single pty.write() occur in one daemon event-loop turn. Generation or revision mismatch, malformed/oversize input, dead/read-only state, or replay rejects with zero bytes. Success advances revision so the same token cannot succeed twice.
Provider-specific idle, composer, hook, human, Codex, Claude, and DING semantics remain outside PTY. Adapters retain inbox ownership when delivery rejects.
Required PTY tests
- unchanged generation/revision succeeds exactly once
- prior ordinary key/newline/paste/ESC-shaped bytes each invalidate the guard without interpreting them
- child output and actual resize races invalidate the guard
- replacement generation rejects
- an attached viewer with no input or resize leaves the token eligible
- malformed, oversize, stale, and replayed guards reject with zero guarded bytes
- failed guard leaves the live activity lease and socket usable
Partial-draft interpretation and DING inbox retention are adapter/evals fixtures; the PTY counterpart proves that any pre-check ordinary input changes revision and makes guarded delivery zero-write.
Draft-only implementation. No merge or deployment.
Problem
A provider adapter can establish fresh idle authority and verify its own empty-composer safety rule, but an ordinary input, child output, resize, or daemon replacement can race between that check and delivery. PTY must not guess what Enter, newline, paste, ESC, or any other bytes mean.
Depends on #129.
Proposed generic seam
Expose top-level daemon
generationplus a monotonic per-generationioRevisionthrough live STATUS. Add one bounded conditional write request and public API:The daemon increments revision for every accepted ordinary or guarded input, every child-output chunk, and every actual PTY size mutation. Attach, peek, and status without input or resizing do not increment it.
Guard validation and the single
pty.write()occur in one daemon event-loop turn. Generation or revision mismatch, malformed/oversize input, dead/read-only state, or replay rejects with zero bytes. Success advances revision so the same token cannot succeed twice.Provider-specific idle, composer, hook, human, Codex, Claude, and DING semantics remain outside PTY. Adapters retain inbox ownership when delivery rejects.
Required PTY tests
Partial-draft interpretation and DING inbox retention are adapter/evals fixtures; the PTY counterpart proves that any pre-check ordinary input changes revision and makes guarded delivery zero-write.
Draft-only implementation. No merge or deployment.