Session::reader_loop in src/client/api.rs:180 resolves successful job.accepted, artifact, and subscription acknowledgements, but on loop exit it drains only pending_jobs at src/client/api.rs:255. It leaves pending_accepted, pending_artifact, and pending_subscribe senders stored in their DashMaps, so callers waiting in invoke, put_artifact, fetch_artifact, or subscribe can wait forever after the transport closes because the oneshot sender is still alive. The same design also ignores a job.failed that arrives before job.accepted, which can happen for invalid agent references in spawn_tool_invoke and for deferred-acceptance paths when credential provisioning or agent resolution fails.
Fix prompt: Change pending request state so every operation has a single completion path that can carry either success or an ARCPError. On reader shutdown, remove and complete all pending accept, artifact, subscribe, and terminal-job entries with ARCPError::Unavailable; on nack or pre-acceptance job.failed, resolve the corresponding pending invocation with a mapped error instead of leaving pending_accepted alive; and on send failure, remove the pending entries inserted before the send. Add timeout-based regression tests for runtime close during each pending operation and for an invalid tool name that returns job.failed before job.accepted.
Session::reader_loopinsrc/client/api.rs:180resolves successfuljob.accepted, artifact, and subscription acknowledgements, but on loop exit it drains onlypending_jobsatsrc/client/api.rs:255. It leavespending_accepted,pending_artifact, andpending_subscribesenders stored in theirDashMaps, so callers waiting ininvoke,put_artifact,fetch_artifact, orsubscribecan wait forever after the transport closes because the oneshot sender is still alive. The same design also ignores ajob.failedthat arrives beforejob.accepted, which can happen for invalid agent references inspawn_tool_invokeand for deferred-acceptance paths when credential provisioning or agent resolution fails.Fix prompt: Change pending request state so every operation has a single completion path that can carry either success or an
ARCPError. On reader shutdown, remove and complete all pending accept, artifact, subscribe, and terminal-job entries withARCPError::Unavailable; onnackor pre-acceptancejob.failed, resolve the corresponding pending invocation with a mapped error instead of leavingpending_acceptedalive; and on send failure, remove the pending entries inserted before the send. Add timeout-based regression tests for runtime close during each pending operation and for an invalid tool name that returnsjob.failedbeforejob.accepted.