MCPcopy Index your code
hub / github.com/docker/docker-agent / Dispatch

Method Dispatch

pkg/hooks/executor.go:170–228  ·  view source on GitHub ↗

Dispatch runs the hooks registered for event and aggregates their verdicts into a single [Result]. Sets input.HookEventName so handlers don't have to remember. Defaults [Input.Cwd] to the executor's working directory when the caller didn't supply one. EventPreToolUsePreYolo is an internal sentinel

(ctx context.Context, event EventType, input *Input)

Source from the content-addressed store, hash-verified

168// handlers) and aggregation reuses the EventPreToolUse branches.
169// Tracing keeps the lane visible via the span name.
170func (e *Executor) Dispatch(ctx context.Context, event EventType, input *Input) (*Result, error) {
171 hooks := e.hooksFor(event, input.ToolName)
172 if len(hooks) == 0 {
173 return &Result{Allowed: true}, nil
174 }
175
176 // Hooks on the preempt-yolo lane see the public pre_tool_use event
177 // name so a single handler implementation works on either lane.
178 publicEvent := event
179 if event == EventPreToolUsePreYolo {
180 publicEvent = EventPreToolUse
181 }
182
183 // Single span per Dispatch call covers every hook the event matched.
184 // Custom name `hook.{event}` because there is no GenAI semconv for
185 // arbitrary user-defined lifecycle hooks; we surface the event type,
186 // matched hook count, and session/agent identifiers so dashboards can
187 // split by event class without parsing span events.
188 ctx, span := otel.Tracer("github.com/docker/docker-agent/pkg/hooks").Start(
189 ctx,
190 "hook."+string(event),
191 trace.WithSpanKind(trace.SpanKindInternal),
192 trace.WithAttributes(
193 attribute.String("cagent.hook.event", string(event)),
194 attribute.Int("cagent.hook.count", len(hooks)),
195 attribute.String("cagent.agent.name", input.AgentName),
196 attribute.String("gen_ai.conversation.id", input.SessionID),
197 ),
198 )
199 if input.ToolName != "" {
200 span.SetAttributes(attribute.String("gen_ai.tool.name", input.ToolName))
201 }
202 defer span.End()
203
204 input.HookEventName = publicEvent
205 if input.Cwd == "" {
206 input.Cwd = e.workingDir
207 }
208
209 slog.DebugContext(ctx, "Executing hooks", "event", event, "session_id", input.SessionID, "count", len(hooks))
210
211 inputJSON, err := input.ToJSON()
212 if err != nil {
213 span.RecordError(err)
214 span.SetStatus(codes.Error, err.Error())
215 return nil, fmt.Errorf("failed to serialize hook input: %w", err)
216 }
217
218 results := make([]hookResult, len(hooks))
219 var wg sync.WaitGroup
220 for i, hook := range hooks {
221 wg.Go(func() { results[i] = e.runHook(ctx, hook, inputJSON) })
222 }
223 wg.Wait()
224
225 final := aggregate(results, event)
226 annotateHookSpan(span, event, final)
227 return final, nil

Calls 12

hooksForMethod · 0.95
runHookMethod · 0.95
aggregateFunction · 0.85
annotateHookSpanFunction · 0.85
ToJSONMethod · 0.80
StartMethod · 0.65
RecordErrorMethod · 0.65
WaitMethod · 0.65
StringMethod · 0.45
SetAttributesMethod · 0.45
EndMethod · 0.45
ErrorMethod · 0.45