feat(daemon): Add slot-based notification deduplication (gt-wpg)

Implement replaceable notifications to prevent heartbeat stacking when
agents are busy. Only the latest notification per slot is delivered.

Changes:
- Add NotificationManager for tracking pending notifications
- Add SendKeysReplace() that clears input line before sending
- Integrate slot tracking into daemon heartbeat pokes
- Mark notifications consumed when agent shows activity

The system tracks pending notifications in state files and skips
sending if a notification for the same slot is still pending.
When agent activity is detected (keepalive), slots are marked
consumed allowing new notifications to be sent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-20 13:19:40 -08:00
parent cc0ee19a8b
commit d2fccd580c
3 changed files with 291 additions and 21 deletions

View File

@@ -138,6 +138,25 @@ func (t *Tmux) SendKeysRaw(session, keys string) error {
return err
}
// SendKeysReplace sends keystrokes, clearing any pending input first.
// This is useful for "replaceable" notifications where only the latest matters.
// Uses Ctrl-U to clear the input line before sending the new message.
// The delay parameter controls how long to wait after clearing before sending (ms).
func (t *Tmux) SendKeysReplace(session, keys string, clearDelayMs int) error {
// Send Ctrl-U to clear any pending input on the line
if _, err := t.run("send-keys", "-t", session, "C-u"); err != nil {
return err
}
// Small delay to let the clear take effect
if clearDelayMs > 0 {
time.Sleep(time.Duration(clearDelayMs) * time.Millisecond)
}
// Now send the actual message
return t.SendKeys(session, keys)
}
// SendKeysDelayed sends keystrokes after a delay (in milliseconds).
// Useful for waiting for a process to be ready before sending input.
func (t *Tmux) SendKeysDelayed(session, keys string, delayMs int) error {