Cancel pending timers when a child is closed
Stale timer bodies were re-delivered to the orchestrator pane after the parent had already processed the sub-agent's reply and called close_process. The timer registry held no link to the child lifecycle, so timers owned by or watching the closed child lingered until something triggered a fire — e.g. a trailing classifier tick for the now-removed child. Add an OnChildClosed hook to ChildEventListener, emit it from Session.Close (and the terminal-corpse path in reapChild), and have the timer manager prune the registry: cancel timers owned by the closed child; remove the closed child from each timer's watched list (cancel the timer outright when watched empties). Natural exit deliberately does not route through this hook — the classifier already emits an idle transition on exit which delivers any legitimate "fire when sub-agent finishes" semantics exactly once; cancelling on exit would swallow that.
This commit is contained in:
@@ -86,10 +86,10 @@ func newToolHost(sess *Session, pads *scratchpad.Store, launcher *Launcher, pres
|
||||
return h
|
||||
}
|
||||
|
||||
// timerListenerAdapter forwards OnChildStateChanged into the timer
|
||||
// manager and ignores the other ChildEventListener methods. The
|
||||
// session's listener API is by-interface, so we wrap the manager
|
||||
// rather than make it implement the full surface.
|
||||
// timerListenerAdapter forwards OnChildStateChanged and OnChildClosed
|
||||
// into the timer manager and ignores the other ChildEventListener
|
||||
// methods. The session's listener API is by-interface, so we wrap
|
||||
// the manager rather than make it implement the full surface.
|
||||
type timerListenerAdapter struct{ m *timerManager }
|
||||
|
||||
func (a timerListenerAdapter) OnChildSpawned(*Child) {}
|
||||
@@ -98,6 +98,9 @@ func (a timerListenerAdapter) OnPTYOut(string, []byte) {}
|
||||
func (a timerListenerAdapter) OnChildStateChanged(id string, st IdleState) {
|
||||
a.m.onChildStateChanged(id, st)
|
||||
}
|
||||
func (a timerListenerAdapter) OnChildClosed(id string) {
|
||||
a.m.onChildClosed(id)
|
||||
}
|
||||
|
||||
func (h *toolHost) SetSize(cols, rows uint16) {
|
||||
h.sizeMu.Lock()
|
||||
@@ -553,6 +556,7 @@ func (n *chunkNotifier) OnPTYOut(id string, chunk []byte) {
|
||||
}
|
||||
}
|
||||
func (n *chunkNotifier) OnChildStateChanged(string, IdleState) {}
|
||||
func (n *chunkNotifier) OnChildClosed(string) {}
|
||||
|
||||
func (h *toolHost) GetProcessPorts(callerID, processID string) ([]mcp.PortSighting, error) {
|
||||
c := h.sess.FindChild(processID)
|
||||
|
||||
Reference in New Issue
Block a user