annotateHookSpan stamps the aggregated verdict onto the hook.{event} span so dashboards can answer "did the hook block this?" and "why?" without re-running the hook. Prior to this the span only carried the event type and hook count — a denied call looked identical to an allowed one. The verdict bool
(span trace.Span, event EventType, r *Result)
| 236 | // PII or LLM output (Message, AdditionalContext, SystemMessage, |
| 237 | // Summary) are gated on the GenAI content-capture opt-in. |
| 238 | func annotateHookSpan(span trace.Span, event EventType, r *Result) { |
| 239 | if span == nil || r == nil { |
| 240 | return |
| 241 | } |
| 242 | attrs := []attribute.KeyValue{ |
| 243 | attribute.Bool("cagent.hook.allowed", r.Allowed), |
| 244 | attribute.Int("cagent.hook.exit_code", r.ExitCode), |
| 245 | } |
| 246 | if r.Decision != "" { |
| 247 | attrs = append(attrs, attribute.String("cagent.hook.decision", string(r.Decision))) |
| 248 | } |
| 249 | if r.DecisionReason != "" { |
| 250 | attrs = append(attrs, attribute.String("cagent.hook.decision_reason", r.DecisionReason)) |
| 251 | } |
| 252 | if event == EventPermissionRequest { |
| 253 | attrs = append(attrs, attribute.Bool("cagent.hook.permission_allowed", r.PermissionAllowed)) |
| 254 | } |
| 255 | if r.ModifiedInput != nil { |
| 256 | attrs = append(attrs, attribute.Bool("cagent.hook.modified_input", true)) |
| 257 | } |
| 258 | if r.Summary != "" { |
| 259 | attrs = append(attrs, attribute.Bool("cagent.hook.summary_provided", true)) |
| 260 | } |
| 261 | if genai.IsContentCaptureEnabled() { |
| 262 | if r.Message != "" { |
| 263 | attrs = append(attrs, attribute.String("cagent.hook.message", r.Message)) |
| 264 | } |
| 265 | if r.AdditionalContext != "" { |
| 266 | attrs = append(attrs, attribute.String("cagent.hook.additional_context", r.AdditionalContext)) |
| 267 | } |
| 268 | if r.SystemMessage != "" { |
| 269 | attrs = append(attrs, attribute.String("cagent.hook.system_message", r.SystemMessage)) |
| 270 | } |
| 271 | if r.Summary != "" { |
| 272 | attrs = append(attrs, attribute.String("cagent.hook.summary", r.Summary)) |
| 273 | } |
| 274 | } |
| 275 | span.SetAttributes(attrs...) |
| 276 | } |
| 277 | |
| 278 | // hooksFor returns the deduplicated list of hooks that should run for |
| 279 | // (event, toolName). Dedup by (type, command, args) catches the common |
no test coverage detected