Documentation
¶
Overview ¶
Package dispatch is the agent's single execution chokepoint. Every action the agent takes - a model call, a tool call, an MCP call, a built-in command - flows through Dispatcher.Govern, where governance, tracing, structured logging, event emission, and hooks are applied once. Callers bring their own work as a closure and inherit all of it; no call site writes its own instrumentation.
The waist is payload-agnostic on purpose. It governs an action's metadata (a name, a scope) and what it cost (Metering), never the work's typed input or output: a model call keeps its llm.Request/Response, a tool keeps its JSON, and dispatch knows neither. That is what lets one bracket govern every kind of action without coupling to any of them.
This is the narrow waist the cross-cutting concerns hang off: design it early so traceability, governance, and replay are uniform rather than retrofitted across scattered call sites.
Index ¶
Constants ¶
const ( EventStart = "dispatch.start" EventEnd = "dispatch.end" EventRejected = "dispatch.rejected" )
Event types.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Action ¶
type Action struct {
Name string
Scope state.Scope
// Trust classifies the work so a containment gate can refuse it on a host that
// cannot isolate it. The zero value is sandbox.TrustTrusted: an action carries the
// agent's own trust unless a call site marks it as model-authored or external.
Trust sandbox.Trust
// Goal is the id of the goal this action runs under, recorded on the lifecycle
// event so a run's per-goal governance posture (which child hit a boundary) is
// derivable from the stream. Empty when a call site does not run under a goal.
Goal string
}
Action is the governed unit's identity. It is metadata only: Name resolves the admission policy and labels the lifecycle event; Scope attributes the action on the spine; Trust is how far the work is trusted, which sets the containment a gate requires before it runs. The work's typed input and output stay with the caller, so dispatch never depends on a tool's or a model's types.
type Admitter ¶
Admitter governs an action before any side effect: capability, budget, and approval checks live here. Returning a non-nil error rejects the action; use fault classes (e.g. NeedsApproval, BudgetExceeded) so callers can react.
type AllowAll ¶
type AllowAll struct{}
AllowAll is an Admitter that admits every action; the standalone default until the governor is wired in.
type DiscardSink ¶
type DiscardSink struct{}
DiscardSink is an EventSink that drops events; the standalone default.
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher is the chokepoint: a per-run governance context that any number of actions Govern through, so model calls, tool calls, and verifications all land on one admitter and one event stream. Construct it with New.
func New ¶
func New(opts ...Option) *Dispatcher
New builds a Dispatcher, filling in standalone defaults so it is usable with zero configuration.
func (*Dispatcher) Govern ¶
func (d *Dispatcher) Govern(ctx context.Context, a Action, work func(context.Context) (Metering, error)) error
Govern runs work through the full waist: Before hooks and admission, then (if admitted) a traced, logged, event-bracketed execution, then After hooks unwound in reverse for the hooks that entered (see Hook). work is opaque: it performs the caller's typed action, captures its result in the caller's own closure, and returns only the Metering to charge. The returned error is work's error (or the rejection that stopped it from running).
type Event ¶
type Event struct {
Type string // EventStart, EventEnd, or EventRejected
Action string
Call int64 // correlation id pairing one invocation's start with its end or rejection
Scope state.Scope
Trust string // the work's trust level, recorded so a run's containment posture is auditable
Goal string // id of the goal the action ran under, empty when it ran under no goal
At int64 // unix nanos, from the dispatcher's clock
Err string // fault class on failure; empty on success
}
Event is one record on the action lifecycle, appended to the spine.
type EventSink ¶
EventSink appends action-lifecycle events. The event spine implements this (internal/spinesink); a DiscardSink or MemorySink is used in tests and headless runs.
type Hook ¶
type Hook interface {
Before(ctx context.Context, a Action) error
After(ctx context.Context, a Action, m Metering, err error)
}
Hook observes dispatch without being a call site, so new cross-cutting behaviour (adversary review, dry-run, redaction) is registered, not edited into every action. Before may reject by returning an error. After runs, in reverse order, for each hook whose Before succeeded - including on rejection or work failure - so a hook can pair Before/After like acquire/release. After receives the Metering the work reported (zero on a rejection).
type MemorySink ¶
type MemorySink struct {
// contains filtered or unexported fields
}
MemorySink is an EventSink that records events in memory. It stands in where no durable spine sink is wired (tests, headless runs) and makes the waist's emissions inspectable. It is safe for concurrent use.
func (*MemorySink) Append ¶
func (s *MemorySink) Append(_ context.Context, e Event) error
Append implements EventSink.
func (*MemorySink) Events ¶
func (s *MemorySink) Events() []Event
Events returns a copy of the recorded events in append order.
type Metering ¶
Metering is what a governed unit reports back for accounting and the end event. The Dispatcher sees only this, never the unit's typed result.
type Option ¶
type Option func(*Dispatcher)
Option configures a Dispatcher.
func WithAdmitter ¶
WithAdmitter sets the governance gate (default: AllowAll).
func WithEventSink ¶
WithEventSink sets the event spine sink (default: DiscardSink).
func WithObservability ¶
func WithObservability(o *observe.Observability) Option
WithObservability sets the logger and tracer (default: observe.Default()).