mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 18:22:38 +00:00
* Let Stop settle an agent whose Pi or OMP runtime has exited AgentSession.interrupt() is documented to reject "only when foreground ownership is still uncertain", and to be a successful no-op when the session is already idle. The Pi and OMP sessions broke that: interrupt() issues `abort` (and `clear_queue` for Pi) over the JSONL RPC transport, and once the child process is gone every request rejects with "<name> process is closed". AgentManager reads that rejection as "the provider still owns the turn", returns `refused`, and leaves the tracked run in place. The user gets "Cannot stop agent <id> because its active run cancellation was not acknowledged", the agent stays pinned at `running`, and pressing Stop again repeats it forever — only a daemon restart clears it. A dead runtime owns nothing: no turn is running and no prompt is queued, which is exactly the postcondition abort and clear_queue promise. Route both through JsonlRpcProcess.requestStopWork, which resolves when the process is gone and still surfaces real failures from a live process. The check covers the process dying while the request is in flight, so a race cannot reintroduce the wedge. * Require an observed child exit before treating stop work as done `disposed` marks the transport finished, not the child gone: `close()` disposes before termination completes, a spawn error disposes without touching the child, and a child can outlive SIGKILL. Keying requestStopWork on it could report a turn canceled while the child was still working. Track the child's exit event instead, and resolve only on that. A disposed transport whose child has not exited still rejects, so the caller keeps refusing a cancellation it cannot prove. Also move the in-flight tests' command detection into named `exitOnCommand` helpers, and reuse the extracted `onOmpCommand` for `replyToCommands` so the OMP file matches the Pi one. * Let a stop survive an stdin failure that precedes the child's exit A dying child breaks its stdin pipe a fraction before it emits `exit`. The broken pipe fails the in-flight abort and starts a termination, and deciding at that moment saw `exited` still false, so the stop was refused for a runtime that was already on its way out. That is the reported wedge again, reached through a narrower race. close() now records the termination it starts, and requestStopWork waits for it before deciding. A child that goes on to exit resolves the stop; a child that outlives SIGKILL still rejects it. --------- Co-authored-by: paseo-bot[bot] <paseo-bot[bot]@users.noreply.github.com>