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 }