processToolCalls builds a per-stream [toolexec.Dispatcher] and delegates the per-call orchestration to it. The dispatcher owns the approval flow, hook dispatch, tracing, telemetry, and event emission; this file only supplies the runtime-side adapters that bridge them to the runtime's chan Event. Th
(ctx context.Context, sess *session.Session, calls []tools.ToolCall, agentTools []tools.Tool, events EventSink)
| 30 | // halts the *batch* but keeps the loop alive so the synthesised tool |
| 31 | // error responses can be sent back to the model on the next turn. |
| 32 | func (r *LocalRuntime) processToolCalls(ctx context.Context, sess *session.Session, calls []tools.ToolCall, agentTools []tools.Tool, events EventSink) (stopRun bool, stopMessage string) { |
| 33 | // Bind runtime-managed handlers (transfer_task, handoff, change_model, ...) |
| 34 | // to the current events channel: r.toolMap entries take chan Event, |
| 35 | // toolexec.ToolHandler doesn't. |
| 36 | handlers := make(map[string]toolexec.ToolHandler, len(r.toolMap)) |
| 37 | for name, h := range r.toolMap { |
| 38 | handlers[name] = func(ctx context.Context, sess *session.Session, tc tools.ToolCall) (*tools.ToolCallResult, error) { |
| 39 | return h(ctx, sess, tc, events) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | d := &toolexec.Dispatcher{ |
| 44 | Tracer: r.tracer, |
| 45 | Hooks: &hookDispatcher{r: r, events: events}, |
| 46 | Resume: r.resumeChan, |
| 47 | AgentFor: r.resolveSessionAgent, |
| 48 | Permissions: r.permissionCheckers, |
| 49 | Handlers: handlers, |
| 50 | } |
| 51 | return d.Process(ctx, sess, calls, agentTools, &sinkEmitter{events: events}) |
| 52 | } |
| 53 | |
| 54 | // permissionCheckers returns the ordered list of permission checkers to |
| 55 | // evaluate (session-level first, then team-level). |