notifyUpdate throttle updates lastNotify on every call — permanently suppresses UI updates during streaming
Severity: Medium
File and Lines
internal/subagent/manager.go, lines ~1085-1100
Problem Description
The notifyUpdate method updates m.lastNotify = now before the throttle check, including on calls that get skipped:
now := time.Now()
lastNotify := m.lastNotify
m.lastNotify = now // ← updated unconditionally
...
if !lastNotify.IsZero() && now.Sub(lastNotify) < 100*time.Millisecond {
return // ← skipped, but lastNotify already advanced
}
fn(sa)
When events arrive at <100ms intervals (normal during streaming — 30-100 tokens/s), lastNotify is always recent enough that now.Sub(lastNotify) is always <100ms. After the first delivery, fn(sa) is never called again as long as events keep arriving.
Trigger Scenario
Any sub-agent streaming output (LLM tokens arriving every 10-50ms). The onUpdate callback fires once, then never again until the event stream pauses for >100ms.
Expected vs Actual
- Expected: ~10 Hz update rate (every 100ms) as documented
- Actual: 0 Hz during continuous streaming — UI shows stale status/phase
Impact
onStreamText is a separate unthrottled channel, so text still streams
- But status/phase/activity updates (tool transitions, progress) are frozen during active streaming
- All callers affected: Cancel, Start, SetActivity, Notify, Complete
Fix Suggestion
Only update m.lastNotify when actually delivering:
if !lastNotify.IsZero() && time.Since(lastNotify) < 100*time.Millisecond {
return // do NOT update m.lastNotify
}
m.lastNotify = time.Now()
fn(sa)
notifyUpdate throttle updates lastNotify on every call — permanently suppresses UI updates during streaming
Severity: Medium
File and Lines
internal/subagent/manager.go, lines ~1085-1100Problem Description
The
notifyUpdatemethod updatesm.lastNotify = nowbefore the throttle check, including on calls that get skipped:When events arrive at <100ms intervals (normal during streaming — 30-100 tokens/s),
lastNotifyis always recent enough thatnow.Sub(lastNotify)is always <100ms. After the first delivery,fn(sa)is never called again as long as events keep arriving.Trigger Scenario
Any sub-agent streaming output (LLM tokens arriving every 10-50ms). The
onUpdatecallback fires once, then never again until the event stream pauses for >100ms.Expected vs Actual
Impact
onStreamTextis a separate unthrottled channel, so text still streamsFix Suggestion
Only update
m.lastNotifywhen actually delivering: