Skip to content

fix(coding-agent): resume suspended input pump on programmatic admission - #895

Open
twaldin wants to merge 2 commits into
PrimeIntellect-ai:mainfrom
twaldin:fix/resume-suspended-input-pump
Open

fix(coding-agent): resume suspended input pump on programmatic admission#895
twaldin wants to merge 2 commits into
PrimeIntellect-ai:mainfrom
twaldin:fix/resume-suspended-input-pump

Conversation

@twaldin

@twaldin twaldin commented Aug 8, 2026

Copy link
Copy Markdown

Problem

Any abort — requestAbort() from Esc/TUI interrupt, a daemon abort, a goal abort, or abortForUpdateRestart() — sets _sessionInputPumpSuspended = true. The user-typed prompt path (_prompt) clears that flag when the session is not streaming, but the programmatic admission path (queueAgentMessagePrompt_queuePreparedPrompt_admitSessionInput) never does. After one abort, every programmatically queued item — agent messages from children, heartbeat prompts, injected wake prompts — sits in the queue forever while the session is idle. Only a human-typed prompt or an explicit resume_queue revives delivery.

Reproduced clean-room on a pristine 0.7.0 install with a stub provider, driven over the daemon socket:

scenario result
long tool call / long streaming turn with items queued delivered at boundary — fine
idle session, items queued delivered immediately — fine
abort during streaming with items queued starved forever (queuedCount persisted >90s idle)
then one user-typed prompt flushes the entire starved backlog in ~2s
starved queue + heartbeat cron due heartbeat silently skipped every fire

The starved queue also blackholes heartbeats: shouldDeferHeartbeatCronJob defers while unfinishedActionCount > 0, and a skipped fire is lost until the next full interval because nextRunAt is recomputed from the skip time (adjacent to #820).

Related: #820 (heartbeat fires dropped when coincident with busy), #821 (send retry duplication), #823 (queued agent mail delivery). None of them names the suspended-pump starvation itself.

Fix

  • _admitSessionInput: when a programmatic turn admission arrives while the session is not streaming and the pump is suspended, clear the suspension, notify checkpoint waiters, and schedule the pump — the same resume semantics _prompt() already gives user-typed input.
  • recordDispatchResult: a skipped heartbeat fire reschedules to the next scheduler tick (1 minute) instead of the next full interval, so a deferred fire retries instead of being lost.

Testing

  • New regression test resume-suspended-input-pump.test.ts: queued agent mail after an idle abort delivers; a backlog queued before an abort drains once a later programmatic admission arrives. Both fail without the fix.
  • Updated cron-jobs.test.ts skip-reschedule expectation to the new retry-next-tick semantics.
  • vitest run on cron-jobs, agent-session-queue, agent-session-prompt, agent-session-action-races: no new failures versus the unpatched baseline.

This PR was prepared with agent assistance.

Note

Fix AgentSession.admitSessionAction to resume suspended input pump on programmatic admission

  • After an abort suspends the input pump, queued agent messages, heartbeats, and wake prompts would starve. This fix resumes the pump on two paths: coalesced follow-up re-fires and direct programmatic admissions of turn actions.
  • Skipped heartbeat cron jobs now reschedule to retry at now + 1 minute instead of waiting for the full schedule interval, preventing long stalls after a skip.
  • Regression tests in resume-suspended-input-pump.test.ts cover the three resume scenarios.
  • Behavioral Change: skipped heartbeat jobs retry sooner than before; non-heartbeat and once-kind jobs retain prior scheduling behavior.

Macroscope summarized 9f77b4d.

twaldin added 2 commits August 7, 2026 18:59
Any abort (requestAbort, abortForUpdateRestart) suspends the session
input pump. A user-typed prompt clears the suspension, but programmatic
admission via queueAgentMessagePrompt/_queuePreparedPrompt never does,
so queued agent messages, heartbeats, and wake prompts starve forever
while the session sits idle. Clear the suspension on programmatic turn
admission when not streaming, mirroring _prompt().

A starved queue also blocked every heartbeat fire, and a skipped
heartbeat lost the fire until the next full interval because nextRunAt
was recomputed from the skip time. A skipped heartbeat now retries at
the next scheduler tick.

Regression tests: queued agent mail post-abort delivers at idle, and a
pre-abort backlog drains once a later programmatic admission arrives.
Both fail on the previous behavior.
A follow-up re-fire that coalesces into an already-queued owner (same
queueKey, e.g. rolling wake prompts or heartbeats) was dropped before the
pump-resume path ran, so the queued owner still starved after an abort
suspended the pump at idle. Resume the pump on the coalesced path too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant