/ internal / daemon / multi_handler.go
multi_handler.go
  1  package daemon
  2  
  3  import (
  4  	"time"
  5  
  6  	"github.com/Kocoro-lab/ShanClaw/internal/agent"
  7  )
  8  
  9  // multiHandler fans agent.EventHandler callbacks to multiple wrapped handlers.
 10  //
 11  // Propagation rules:
 12  //   - Base methods (OnToolCall, OnText, OnUsage, etc.): call every wrapped handler in order.
 13  //   - OnApprovalNeeded: call every wrapped handler; return the OR of all results.
 14  //     This means any handler returning "approved" approves the call.
 15  //   - SetSessionID (Task 8): propagate only to wrapped handlers that implement it.
 16  //   - OnRunStatus (Task 9): propagate only to wrapped handlers that implement RunStatusHandler.
 17  type multiHandler struct {
 18  	handlers []agent.EventHandler
 19  }
 20  
 21  func (m *multiHandler) OnToolCall(name, args string) {
 22  	for _, h := range m.handlers {
 23  		h.OnToolCall(name, args)
 24  	}
 25  }
 26  
 27  func (m *multiHandler) OnToolResult(name, args string, result agent.ToolResult, elapsed time.Duration) {
 28  	for _, h := range m.handlers {
 29  		h.OnToolResult(name, args, result, elapsed)
 30  	}
 31  }
 32  
 33  func (m *multiHandler) OnText(text string) {
 34  	for _, h := range m.handlers {
 35  		h.OnText(text)
 36  	}
 37  }
 38  
 39  func (m *multiHandler) OnStreamDelta(delta string) {
 40  	for _, h := range m.handlers {
 41  		h.OnStreamDelta(delta)
 42  	}
 43  }
 44  
 45  func (m *multiHandler) OnApprovalNeeded(tool, args string) bool {
 46  	approved := false
 47  	for _, h := range m.handlers {
 48  		if h.OnApprovalNeeded(tool, args) {
 49  			approved = true
 50  		}
 51  	}
 52  	return approved
 53  }
 54  
 55  func (m *multiHandler) OnUsage(u agent.TurnUsage) {
 56  	for _, h := range m.handlers {
 57  		h.OnUsage(u)
 58  	}
 59  }
 60  
 61  func (m *multiHandler) OnCloudAgent(agentID, status, message string) {
 62  	for _, h := range m.handlers {
 63  		h.OnCloudAgent(agentID, status, message)
 64  	}
 65  }
 66  
 67  func (m *multiHandler) OnCloudProgress(completed, total int) {
 68  	for _, h := range m.handlers {
 69  		h.OnCloudProgress(completed, total)
 70  	}
 71  }
 72  
 73  func (m *multiHandler) OnCloudPlan(planType, content string, needsReview bool) {
 74  	for _, h := range m.handlers {
 75  		h.OnCloudPlan(planType, content, needsReview)
 76  	}
 77  }
 78  
 79  // SetSessionID propagates the session ID to every wrapped handler that
 80  // implements the optional interface. Handlers that don't implement it are
 81  // skipped silently — matching how RunAgent itself type-asserts the top-level
 82  // handler (runner.go SetSessionID injection path).
 83  func (m *multiHandler) SetSessionID(id string) {
 84  	for _, h := range m.handlers {
 85  		if setter, ok := h.(interface{ SetSessionID(string) }); ok {
 86  			setter.SetSessionID(id)
 87  		}
 88  	}
 89  }
 90  
 91  // OnRunStatus propagates watchdog/retry events to wrapped handlers that
 92  // implement agent.RunStatusHandler. The method is present on multiHandler
 93  // itself (even though it's optional for arbitrary EventHandlers) so that
 94  // the agent loop's type assertion `a.handler.(RunStatusHandler)` succeeds
 95  // when the loop handler is a multiHandler.
 96  func (m *multiHandler) OnRunStatus(code, detail string) {
 97  	for _, h := range m.handlers {
 98  		if rsh, ok := h.(agent.RunStatusHandler); ok {
 99  			rsh.OnRunStatus(code, detail)
100  		}
101  	}
102  }