Documentation
¶
Overview ¶
Package federation implements the v0.1 slice of docs/federation-design.md: the frozen Adapter interface, reference parsing for `<scheme>://<name>[/<skill>]` remote-agent references, a scheme-keyed adapter registry, and the planner-facing `invoke_remote_agent` tool.
The frozen Adapter shape, and why ¶
docs/fork-design.md P1.4 requires the interface be "frozen with an event/interrupt channel in mind — so v0.2's streaming/HITL propagation isn't a breaking change". The docs flagged signature churn as the risk, so the shape choice is load-bearing. Three candidate shapes were considered:
Invoke returns (*Result, error) directly. Rejected: it hard-wires "the invocation is finished when Invoke returns". v0.2 streaming must deliver events BEFORE the terminal result exists, so this shape forces either a signature change or a semantics change (Invoke returning a half-populated Result) — exactly the churn we're freezing against.
Invoke takes a callback/event-sink field on InvokeOptions. Rejected: adding the field later is non-breaking structurally, but it inverts the data flow (push into caller-supplied sink vs. pull from the invocation) and gives interrupts/HITL no natural home — a remote `input-required` pause needs a bidirectional handle, not a write-only sink.
Invoke returns a Handle (chosen). The Handle carries the whole post-dispatch lifecycle: Wait for the terminal Result, Events for intermediate updates, Cancel for remote cancellation. v0.1 adapters do all work synchronously inside Invoke (v0.1 blocks to a bounded timeout, per docs/durable-execution-design.md phasing) and return an already-resolved Handle; v0.2 adapters return a live Handle whose Events stream and whose Wait blocks. Callers written against Wait/Events today keep compiling AND keep their semantics — only WHERE the waiting happens moves. Interrupt propagation (v0.2 HITL) lands as a new Event type plus a response method on a Handle extension interface, not as a signature edit.
Contract split: Invoke returns a non-nil error only for dispatch-time failures (unresolvable reference, unknown agent, invalid options). Execution failures — transport errors, remote task failure, timeout — surface from Handle.Wait, so callers have exactly one place to handle them regardless of whether the adapter is synchronous or streaming.
This shape freezes at v0.1. Additive evolution only: new fields on InvokeOptions/Result/Event structs, new Event types, extension interfaces on Handle.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnknownScheme — no adapter registered for the reference scheme. ErrUnknownScheme = errors.New("federation: unknown reference scheme") // ErrUnknownAgent — the scheme resolved but the named agent is not // configured (e.g. no matching .agents/a2a/*.yaml). ErrUnknownAgent = errors.New("federation: unknown remote agent") // ErrInvalidReference — the reference string does not parse against // the `<scheme>://<name>[/<skill>]` grammar. ErrInvalidReference = errors.New("federation: invalid reference") // ErrUnreachable — network partition, DNS failure, endpoint down. ErrUnreachable = errors.New("federation: remote agent unreachable") // ErrTimeout — the bounded v0.1 wait expired before the remote // reached a terminal state. ErrTimeout = errors.New("federation: remote invocation timed out") // ErrAuthFailed — the remote rejected our credentials (401/403) or // the configured credential source is unusable. ErrAuthFailed = errors.New("federation: remote authentication failed") // ErrProtocolMismatch — the remote does not speak a transport this // adapter supports (e.g. an agent card advertising only gRPC). ErrProtocolMismatch = errors.New("federation: protocol mismatch") // ErrRemoteFailed — the remote agent reached a non-success terminal // state (failed / rejected / canceled remotely). ErrRemoteFailed = errors.New("federation: remote invocation failed") )
Sentinel errors, named per the docs/federation-design.md failure-mode table. Adapters wrap these so callers can errors.Is across protocols.
Functions ¶
func NewInvokeRemoteAgentTool ¶
NewInvokeRemoteAgentTool builds the single unified planner tool `invoke_remote_agent(reference, inputs)` over a Registry. cmd/mast (or a library consumer) constructs the Registry with the adapters the deployment permits and adds the returned tool to the planner's tool list; this package does no global registration.
v0.1 semantics: the call blocks to a bounded timeout (the remote agent config's timeout; docs/a2a-design.md v0.1 phasing row) and the tool result carries only the terminal state — intermediate events and HITL propagation arrive with v0.2 per the frozen Handle contract.
Types ¶
type Adapter ¶
type Adapter interface {
// Scheme returns the reference scheme this adapter serves ("a2a",
// "mast", "http", ...). Must be non-empty, lowercase, and stable.
Scheme() string
// Invoke dispatches inputs to the remote agent identified by ref.
// It returns an error only for dispatch-time failures (resolution,
// validation); execution errors surface from Handle.Wait. v0.1
// adapters MAY complete the entire remote interaction inside Invoke
// (bounded by ctx and opts.Timeout) and return a resolved Handle.
Invoke(ctx context.Context, ref Reference, inputs map[string]any, opts InvokeOptions) (Handle, error)
}
Adapter is the protocol extension point from docs/federation-design.md. One adapter per reference scheme; the registry dispatches on Reference.Scheme. FROZEN at v0.1 — see the package documentation for the compatibility contract.
type Event ¶
type Event struct {
// Type discriminates the event ("status", "artifact",
// "input-required", ...). Namespaced growth, additive-only.
Type string
// Data is the type-specific payload.
Data map[string]any
}
Event is one intermediate update from a remote invocation. v0.1 defines the envelope only — no adapter produces events yet. v0.2 (streaming, HITL propagation) adds Type values; consumers must ignore Types they do not recognize.
type Handle ¶
type Handle interface {
// Wait blocks until the invocation reaches a terminal state and
// returns the Result, or the terminal error. Wait is idempotent:
// subsequent calls return the same outcome. ctx bounds the wait
// only; canceling it does not by itself cancel the remote task
// (use Cancel).
Wait(ctx context.Context) (*Result, error)
// Events returns the intermediate-event stream. v0.1 adapters
// return an already-closed channel (no events precede the terminal
// state in synchronous mode); v0.2 streaming adapters deliver task
// updates here. The channel is closed when the invocation reaches a
// terminal state. Never returns nil.
Events() <-chan Event
// Cancel requests cancellation of the remote work. Idempotent;
// best-effort. After a successful Cancel, Wait returns an error
// wrapping ErrRemoteFailed (or the remote's terminal outcome if it
// won the race).
Cancel(ctx context.Context) error
}
Handle is the post-dispatch lifecycle of one remote invocation.
func NewResolvedHandle ¶
NewResolvedHandle wraps an already-terminal outcome in a Handle. It is the intended return path for v0.1 synchronous adapters (and for tests). Exactly one of res/err is meaningful; err wins when non-nil.
type InvokeOptions ¶
type InvokeOptions struct {
// Timeout bounds the whole invocation (dispatch through terminal
// state). Zero means the adapter's / remote-agent config's default.
Timeout time.Duration
}
InvokeOptions carries per-invocation knobs. A zero value is valid. Growth is additive-only (new fields, never changed ones).
type InvokeRemoteAgentArgs ¶
type InvokeRemoteAgentArgs struct {
// Reference identifies the remote agent:
// <scheme>://<name>[/<skill>], e.g. a2a://external-triage/investigate-incident.
Reference string `json:"reference"`
// Inputs is the structured input payload for the remote agent.
Inputs map[string]any `json:"inputs,omitempty"`
}
InvokeRemoteAgentArgs is the LLM-facing argument schema for the invoke_remote_agent planner tool (docs/federation-design.md: "the planner's tool vocabulary gains one class").
type Reference ¶
type Reference struct {
// Scheme selects the adapter ("a2a", and in later versions "mast",
// "http", "grpc"). Always lowercase.
Scheme string
// Name is the configured remote-agent name (URI host position).
// Always lowercase.
Name string
// Skill is the optional skill selector. Empty means "the agent's
// default / sole skill".
Skill string
// Raw is the reference string as given.
Raw string
}
Reference is a parsed remote-agent reference per the docs/federation-design.md grammar. v0.1 accepts both spellings the design corpus uses for skill selection:
<scheme>://<name>/<skill> (docs/a2a-design.md) <scheme>://<name>?skill=<s> (docs/federation-design.md)
Supplying both is an error rather than a silent precedence rule. Parsing is standard-URI (docs/federation-design.md open question 1's bias: "use standard URI parsing; scheme becomes the adapter selector"), which means the name occupies the URI host position and is therefore case-insensitive per RFC 3986 — Parse normalizes it to lowercase, and agent config names must be lowercase to match (the pkg/a2a config loader enforces this). ParseReference applies the lowercase normalization itself — net/url preserves host case on parse, but two references differing only in name case MUST resolve identically, so normalization happens here, once.
func ParseReference ¶
ParseReference parses a remote-agent reference. All parse failures wrap ErrInvalidReference.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps reference schemes to Adapters and dispatches invocations. Safe for concurrent use.
func NewRegistry ¶
NewRegistry returns a Registry with the given adapters registered. It panics on the same conditions Register rejects — construction with a duplicate or invalid scheme is a programming error, not a runtime condition.
func (*Registry) Adapter ¶
Adapter returns the adapter registered for scheme, or an error wrapping ErrUnknownScheme.
func (*Registry) Invoke ¶
func (r *Registry) Invoke(ctx context.Context, raw string, inputs map[string]any, opts InvokeOptions) (Handle, error)
Invoke parses raw, resolves the adapter by scheme, and dispatches. Same error contract as Adapter.Invoke: dispatch-time failures here, execution failures from Handle.Wait.
type Result ¶
type Result struct {
// State is the terminal state as reported by the remote, normalized
// to the remote protocol's vocabulary (A2A: "completed"; a direct
// message reply also reports "completed").
State string
// RemoteID identifies the remote unit of work when the protocol has
// one (A2A task ID). Empty for direct request/response replies.
RemoteID string
// Text is the concatenated human-readable output (A2A text parts).
Text string
// Output is the structured output (A2A data parts, merged in
// arrival order — later keys win).
Output map[string]any
// Raw is the protocol-level terminal payload, for debugging and for
// callers that need fields Result does not model.
Raw json.RawMessage
}
Result is the terminal outcome of a remote invocation, protocol agnostic. Growth is additive-only.