Keep pi alive while it is actively streaming (inactivity timeout) #18

Closed
opened 2026-07-05 07:26:22 +08:00 by weiwen · 1 comment
Owner

What to build

Today evie kills the pi subprocess whenever a single response exceeds a fixed wall-clock deadline (default 60s), even when pi is actively streaming events the whole time. Long but healthy requests (e.g. a slow tool call) get guillotined mid-progress.

Replace the whole-response deadline with an inactivity timeout: the clock is measured against silence from pi, not total elapsed time. Any line pi emits on its stdout event stream — a recognized event, an unrecognized/Other event, or even a malformed line — is a sign of life and resets the timer. Pi is only reaped after a full window elapses with zero output. This also preserves protection against a pi that never responds at all (the first read is under the same timeout).

Mechanically this means the timeout wraps each individual line read inside the response loop rather than wrapping the entire read-until-agent_end future.

Rename the config field that controls this from response_timeout_secs to response_idle_timeout_secs (value stays 60) and update its doc comment to describe the new semantics ("max seconds of silence from pi before it is treated as hung; resets on any activity"). Update all sample config blocks and the session-layer plumbing that carries this value.

Acceptance criteria

  • A response where pi keeps emitting events for well beyond the old 60s deadline is NOT terminated
  • A pi process that goes fully silent for the configured window IS reaped, and the session is cleaned up as before
  • A pi that never emits anything is still reaped after one window (start-of-response hang protection retained)
  • Config field renamed to response_idle_timeout_secs with updated doc comment; all sample config blocks + session plumbing updated; no stale response_timeout_secs references remain
  • Tests cover: activity resets the timer, and silence trips it (best-effort using paused time + a stalling reader); existing pi.rs response-parsing tests still pass
  • cargo build, cargo test, cargo clippy are clean

Blocked by

None - can start immediately

## What to build Today evie kills the pi subprocess whenever a single response exceeds a fixed wall-clock deadline (default 60s), even when pi is actively streaming events the whole time. Long but healthy requests (e.g. a slow tool call) get guillotined mid-progress. Replace the whole-response deadline with an **inactivity timeout**: the clock is measured against silence from pi, not total elapsed time. Any line pi emits on its stdout event stream — a recognized event, an unrecognized/`Other` event, or even a malformed line — is a sign of life and resets the timer. Pi is only reaped after a full window elapses with *zero* output. This also preserves protection against a pi that never responds at all (the first read is under the same timeout). Mechanically this means the timeout wraps each individual line read inside the response loop rather than wrapping the entire read-until-`agent_end` future. Rename the config field that controls this from `response_timeout_secs` to `response_idle_timeout_secs` (value stays 60) and update its doc comment to describe the new semantics ("max seconds of silence from pi before it is treated as hung; resets on any activity"). Update all sample config blocks and the session-layer plumbing that carries this value. ## Acceptance criteria - [ ] A response where pi keeps emitting events for well beyond the old 60s deadline is NOT terminated - [ ] A pi process that goes fully silent for the configured window IS reaped, and the session is cleaned up as before - [ ] A pi that never emits anything is still reaped after one window (start-of-response hang protection retained) - [ ] Config field renamed to `response_idle_timeout_secs` with updated doc comment; all sample config blocks + session plumbing updated; no stale `response_timeout_secs` references remain - [ ] Tests cover: activity resets the timer, and silence trips it (best-effort using paused time + a stalling reader); existing pi.rs response-parsing tests still pass - [ ] `cargo build`, `cargo test`, `cargo clippy` are clean ## Blocked by None - can start immediately
Author
Owner

Implemented per-line inactivity timeout on branch sandcastle/issue-18 (commit 14a5517).

What changed:

  • src/pi.rs: read_agent_response now accepts idle_timeout: Duration and wraps each reader.read_line() individually with tokio::time::timeout. Any stdout output resets the clock; only a full silent window trips it. The outer whole-response timeout() wrapper in send_message is removed.

  • response_timeout_secsresponse_idle_timeout_secs in config.rs, session.rs, main.rs, README.md, DESIGN.md, CONTEXT.md (comment updated to explain inactivity semantics).

  • Three new tests using #[tokio::test(start_paused = true)]: silent-from-start trips timeout, activity-then-silence trips timeout, continuous activity does not trip.

Note: No Rust toolchain in this sandbox; CI will validate compilation and tests.

Implemented per-line inactivity timeout on branch `sandcastle/issue-18` (commit `14a5517`). **What changed:** - `src/pi.rs`: `read_agent_response` now accepts `idle_timeout: Duration` and wraps each `reader.read_line()` individually with `tokio::time::timeout`. Any stdout output resets the clock; only a full silent window trips it. The outer whole-response `timeout()` wrapper in `send_message` is removed. - `response_timeout_secs` → `response_idle_timeout_secs` in `config.rs`, `session.rs`, `main.rs`, `README.md`, `DESIGN.md`, `CONTEXT.md` (comment updated to explain inactivity semantics). - Three new tests using `#[tokio::test(start_paused = true)]`: silent-from-start trips timeout, activity-then-silence trips timeout, continuous activity does not trip. Note: No Rust toolchain in this sandbox; CI will validate compilation and tests.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
weiwen/evie#18
No description provided.