dispatch

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 8 Imported by: 0

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

View Source
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

type Admitter interface {
	Admit(ctx context.Context, a Action) error
}

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.

func (AllowAll) Admit

Admit implements Admitter.

type DiscardSink

type DiscardSink struct{}

DiscardSink is an EventSink that drops events; the standalone default.

func (DiscardSink) Append

Append implements EventSink.

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

type EventSink interface {
	Append(ctx context.Context, e Event) error
}

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

type Metering struct {
	Tokens int
	Cost   float64
}

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

func WithAdmitter(a Admitter) Option

WithAdmitter sets the governance gate (default: AllowAll).

func WithClock

func WithClock(c clock.Clock) Option

WithClock sets the time source (default: clock.System).

func WithEventSink

func WithEventSink(s EventSink) Option

WithEventSink sets the event spine sink (default: DiscardSink).

func WithHook

func WithHook(h Hook) Option

WithHook appends a lifecycle hook.

func WithObservability

func WithObservability(o *observe.Observability) Option

WithObservability sets the logger and tracer (default: observe.Default()).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL