mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(agent): hold input during a guarded submit instead of refusing it
The input lease is held across the awaited tmux Enter, so WebSocket frames keep arriving while it is held. A tagged frame (seq N) was refused and its seq rolled back, but the browser's WebSocket path does not wait for ACKs: seq N+1 landed after the lease and moved the watermark past N, and the redelivered N was then dropped as a duplicate. The keystroke was lost. Input that arrives during the lease is now held and written in arrival order when the lease is released, whatever the submit's outcome. A WebSocket frame is applied and ACKed only once written; managed writes (writeViaMux) and named keys (send-key) wait for their turn the same way, and a held write that is still being sent holds everything behind it. A keystroke held before the guard runs still moves the input revision, so it still invalidates the Enter. The hold is capped per lease (256 inputs, 1 MiB): past it the WebSocket is closed with 1013 and nothing later from it is applied, so the browser resends every unACKed frame in order, and send-key answers 409 CONFLICT instead of logging a tmux failure as INTERNAL_ERROR. POST .../input keeps its 409 for the lease plus the held input's drain; its browser drainer is serialized. Docs: correct the api-reference paragraph that said these keystrokes are redelivered, and describe the capture fallback and the per-scope cursor cap from the previous two commits in api-reference, architecture-invariants and the changeset.
This commit is contained in:
1 parent
ecaddb40ae
commit
2aad93914e
9 files changed
+727
-100
No files matched your search
+22
-12
@@ -427,8 +427,9 @@ turn that never started. Verified live; this is the most common silent failure o
|
||||
this endpoint.
|
||||
|
||||
Any input, waiting or not, answers `409 CONFLICT` with nothing written while an
|
||||
experimental guarded submit holds the session's input lease (a few milliseconds;
|
||||
see [guarded input](#experimental-bounded-agent-inspection-and-guarded-input)).
|
||||
experimental guarded submit holds the session's input lease, or input held behind
|
||||
it is still being written (a few milliseconds; see
|
||||
[guarded input](#experimental-bounded-agent-inspection-and-guarded-input)).
|
||||
Retry it.
|
||||
|
||||
A **plain prompt** (printable text followed by exactly one `\r`, nothing else) is
|
||||
@@ -659,9 +660,10 @@ thread, task completion or authorization.
|
||||
|
||||
Opaque server-side cursors bind caller, session, pane generation, conversation,
|
||||
opened-file identity, byte offset and bounded content anchors. Terminal resizes
|
||||
do not change this inspection scope. At most 1024 live
|
||||
cursors are retained for 15 minutes; restart, eviction or expiry requires a fresh
|
||||
tail baseline. Replacement, truncation, detected rewrites, reassociation, foreign
|
||||
do not change this inspection scope. Each owner and session keeps its newest 8
|
||||
cursors (a retry of the last call still works; older ones are retired), at most
|
||||
1024 live cursors are retained in all, each for 15 minutes; restart, eviction or
|
||||
expiry requires a fresh tail baseline. Replacement, truncation, detected rewrites, reassociation, foreign
|
||||
cursors or loss of the attributed source return `409 CONFLICT`. No transcript
|
||||
filesystem path is returned as cursor/identity metadata; evidence retains the
|
||||
owner's native record content, which can itself contain paths.
|
||||
@@ -708,13 +710,21 @@ may already be in the composer: inspect the pane and follow the stranded-draft
|
||||
recovery (Ctrl+U) in the skill reference before staging again.
|
||||
|
||||
While a guarded submit holds the input lease (the few milliseconds of its
|
||||
conditional Enter), every other Codeman write path refuses, and
|
||||
`POST /api/v1/sessions/:id/input` answers `409 CONFLICT` ("A guarded submit is in
|
||||
progress on this session; nothing was written") instead of a `200` for bytes
|
||||
that were never written. A tagged `clientId`/`seq` frame keeps its seq, so the
|
||||
browser's durable queue, which keeps a frame on a `409`, delivers it on the next
|
||||
retry. WebSocket keystrokes in that window are left unacknowledged and redelivered
|
||||
the same way.
|
||||
conditional Enter), input that reaches the session is held, not refused, and is
|
||||
written in arrival order as soon as the lease is released, whatever the submit's
|
||||
outcome. That covers terminal WebSocket keystrokes (each frame is acknowledged
|
||||
only once it is written, so a frame typed during the lease lands before the one
|
||||
typed after it), Shift+Enter and Ctrl+Enter from `POST .../send-key`, approval
|
||||
answers and Codeman's own managed writes. A held write that is still being sent
|
||||
holds everything behind it. `POST /api/v1/sessions/:id/input` instead answers
|
||||
`409 CONFLICT` ("A guarded submit is in progress on this session; nothing was
|
||||
written") for that span, the lease plus the held input's drain, rather than a
|
||||
`200` for bytes that were never written; a tagged `clientId`/`seq` frame keeps
|
||||
its seq, so the browser's durable queue, whose POST fallback sends one frame at a
|
||||
time and keeps it on a `409`, delivers it on the next retry. A lease holds at
|
||||
most 256 inputs or 1 MiB; past that a WebSocket is closed with code `1013` and
|
||||
nothing later from it is applied, so the browser reconnects and resends every
|
||||
unacknowledged frame in order, and `send-key` answers `409 CONFLICT`.
|
||||
|
||||
Direct auxiliary key operations participate in the same pending-write and input
|
||||
lease checks. Complete allowlisted focus/mouse reports and empty writes preserve
|
||||
|
||||
Reference in new issue
Block a user