Before submitting
Area
apps/server
Steps to reproduce
- Start a long-running background agent task in a thread — a dynamic workflow or a task that fans out subagents that keep working after the foreground turn settles.
- Let the foreground turn complete so the thread has no active user turn, while the background work keeps running.
- Leave the thread otherwise idle (no new
sendTurn) for longer than the reaper's inactivity threshold (default 30 min; swept every 5 min).
- Observe that
ProviderSessionReaper calls stopSession for the thread and tears down the provider session while the background agents are still in flight.
Expected behavior
A session with live background agent work should not be reaped for inactivity. The reaper already skips sessions with an active foreground turn; the same protection should extend to in-flight background work.
Actual behavior
ProviderSessionReaper (apps/server/src/provider/Layers/ProviderSessionReaper.ts) stops any provider session after 30 min of last_seen_at inactivity. Its only busy-guard is the projection snapshot's activeTurnId. When the foreground turn completes, the adapter session goes ready with no active turn, and nothing refreshes last_seen_at from ongoing background activity. So a long-running background workflow with no foreground turn is torn down mid-run. The next interaction resumes the conversation from a transcript cursor as a fresh process, but the in-flight background orchestration (subagent work, queued steps) is gone, leaving the run half-completed.
Impact
Major degradation or frequent failure
Version or commit
main at 5d34f9ff2
Environment
T3 Code server, running background dynamic workflows / subagents.
Logs or stack traces
No exception is thrown. The reaper logs provider.session.reaped with reason: "inactivity_threshold" for a thread whose adapter session is still running background work.
Screenshots, recordings, or supporting files
A deterministic regression test is included in the linked fix.
Workaround
Send a message in the thread periodically (every <30 min) to refresh last_seen_at and keep the session alive while the background work runs.
Suggested fix
Refresh last_seen_at from runtime activity: bump the binding's timestamp when the adapter emits runtime events (e.g. task.progress from background subagents), throttled per thread so an event burst is at most one lightweight write per window. The adapter keeps emitting runtime events during background work even with no active foreground turn, so this keeps the session warm exactly when the reaper would otherwise reap it.
Before submitting
Area
apps/server
Steps to reproduce
sendTurn) for longer than the reaper's inactivity threshold (default 30 min; swept every 5 min).ProviderSessionReapercallsstopSessionfor the thread and tears down the provider session while the background agents are still in flight.Expected behavior
A session with live background agent work should not be reaped for inactivity. The reaper already skips sessions with an active foreground turn; the same protection should extend to in-flight background work.
Actual behavior
ProviderSessionReaper(apps/server/src/provider/Layers/ProviderSessionReaper.ts) stops any provider session after 30 min oflast_seen_atinactivity. Its only busy-guard is the projection snapshot'sactiveTurnId. When the foreground turn completes, the adapter session goesreadywith no active turn, and nothing refresheslast_seen_atfrom ongoing background activity. So a long-running background workflow with no foreground turn is torn down mid-run. The next interaction resumes the conversation from a transcript cursor as a fresh process, but the in-flight background orchestration (subagent work, queued steps) is gone, leaving the run half-completed.Impact
Major degradation or frequent failure
Version or commit
mainat5d34f9ff2Environment
T3 Code server, running background dynamic workflows / subagents.
Logs or stack traces
No exception is thrown. The reaper logs
provider.session.reapedwithreason: "inactivity_threshold"for a thread whose adapter session is still running background work.Screenshots, recordings, or supporting files
A deterministic regression test is included in the linked fix.
Workaround
Send a message in the thread periodically (every <30 min) to refresh
last_seen_atand keep the session alive while the background work runs.Suggested fix
Refresh
last_seen_atfrom runtime activity: bump the binding's timestamp when the adapter emits runtime events (e.g.task.progressfrom background subagents), throttled per thread so an event burst is at most one lightweight write per window. The adapter keeps emitting runtime events during background work even with no active foreground turn, so this keeps the session warm exactly when the reaper would otherwise reap it.