Documentation
¶
Overview ¶
Package broker provides a harness-agnostic run broker for agent communication. It handles run registration, control message queuing, report/finish/reply ingestion, and heartbeat tracking scoped by run ID.
Harness-specific concerns (e.g., MCP sidecar registration, tmux bootstrap, .mcp.json entries) live in the individual harness adapter packages, not here.
Index ¶
- Constants
- func EnrichStopReason(b *Broker, brokerRunID string, stopReason *string)
- func MakeToken() string
- type AgentMessage
- type AskEdge
- type AskReply
- type Broker
- func (b *Broker) Addr() string
- func (b *Broker) CreateRun(runID string) (string, error)
- func (b *Broker) DeleteRun(runID string)
- func (b *Broker) DrainAgentMessages(runID string) ([]ControlMessage, error)
- func (b *Broker) EnsureRun(runID string) (string, bool)
- func (b *Broker) FinishCount(runID string) int
- func (b *Broker) GetRun(runID string) *RunState
- func (b *Broker) Ingest(runID string, kind string, payload json.RawMessage) error
- func (b *Broker) LastFinishStatus(runID string) string
- func (b *Broker) PollAgentMessages(ctx context.Context, runID string, onMessage func(wrappedContent string))
- func (b *Broker) PushControl(runID string, msg ControlMessage) error
- func (b *Broker) Reset()
- func (b *Broker) RunCount() int
- func (b *Broker) Send(runID string, kind string, payload json.RawMessage, correlationID string) error
- func (b *Broker) SendTo(fromRunID, toRunID, kind string, payload json.RawMessage, correlationID string) error
- func (b *Broker) Start() error
- func (b *Broker) Stop() error
- func (b *Broker) UpdateSessionInfo(runID string, info *SessionInfo)
- type ControlMessage
- type Envelope
- type Finish
- type PermissionState
- type Recorder
- type Reply
- type Report
- type RunState
- type SessionInfo
Constants ¶
const ( // DefaultAskTimeout is how long a sender waits for a reply before // the ask edge is pruned and the waiter unblocks with a timeout. DefaultAskTimeout = 10 * time.Minute )
Variables ¶
This section is empty.
Functions ¶
func EnrichStopReason ¶
EnrichStopReason supplements a stop reason with the last finish status from a broker run, when the stop reason is empty and broker state is available. It is safe to call when broker is nil or brokerRunID is empty.
Types ¶
type AgentMessage ¶
type AgentMessage struct {
// ID is a stable message UUID for deduplication, cancellation, and
// ask/reply correlation. Generated by the sender; should be unique
// per sender.
ID string `json:"id,omitempty"`
// From is the display name of the sender run (often same as FromRunID).
From string `json:"from"`
// FromRunID is the run ID of the sending agent.
FromRunID string `json:"from_run_id"`
// ToRunID is the run ID of the intended recipient.
ToRunID string `json:"to_run_id,omitempty"`
// Message is the body text.
Message string `json:"message"`
// Role is a display label such as "agent", "reviewer", or "supervisor".
Role string `json:"role,omitempty"`
// ReplyTo, when non-empty, marks this message as a reply to the
// original message with the given ID. The broker uses this to match
// a waiting ask.
ReplyTo string `json:"reply_to,omitempty"`
// ExpectsReply, when true, signals that the sender is blocking and
// expects a reply with ReplyTo pointing back to this message's ID.
ExpectsReply bool `json:"expects_reply,omitempty"`
}
AgentMessage is the payload for agent-to-agent channel messages. It is used as the payload of "agent_message" control messages pushed via SendTo.
The ID, ReplyTo, and ExpectsReply fields support blocking ask/reply coordination: the sender sets ExpectsReply=true and a stable ID, the receiver replies by sending a new AgentMessage with ReplyTo set to the original ID, and the broker routes the reply back to the waiting sender via the /wait_reply long-poll endpoint.
type AskEdge ¶ added in v0.28.1
type AskEdge struct {
FromRunID string `json:"from_run_id"`
ToRunID string `json:"to_run_id"`
MessageID string `json:"message_id"`
CreatedAt time.Time `json:"created_at"`
}
AskEdge tracks a pending ask (blocking request for reply).
type AskReply ¶ added in v0.28.1
type AskReply struct {
MessageID string `json:"message_id"`
FromRunID string `json:"from_run_id"`
Payload json.RawMessage `json:"payload,omitempty"`
}
AskReply carries a reply routed to a waiting sender.
type Broker ¶
type Broker struct {
// contains filtered or unexported fields
}
Broker is a harness-agnostic, in-process HTTP server that coordinates communication between agent harnesses and their orchestrators. It owns run-scoped state, message queuing, and lifecycle event ingestion.
func New ¶
New creates a broker on an ephemeral loopback port. The resulting addr is available after Start.
func (*Broker) DeleteRun ¶
DeleteRun removes a run from the broker. Safe to call even if the run does not exist.
func (*Broker) DrainAgentMessages ¶ added in v0.28.1
func (b *Broker) DrainAgentMessages(runID string) ([]ControlMessage, error)
DrainAgentMessages returns and clears the agent_message control messages queued for runID (asks / sends from peers). Non-agent control messages are left untouched so other consumers still see them.
func (*Broker) EnsureRun ¶
EnsureRun registers a new run if one does not already exist for the given runID. Returns the run's token (existing or newly created) and whether the run was newly created.
func (*Broker) FinishCount ¶
func (*Broker) Ingest ¶
Ingest routes an incoming message from an agent into the appropriate RunState bucket based on kind.
Known kinds:
- "done", "failed", "blocked" → stored in Finishes
- everything else → stored in Reports
func (*Broker) LastFinishStatus ¶
func (*Broker) PollAgentMessages ¶
func (b *Broker) PollAgentMessages(ctx context.Context, runID string, onMessage func(wrappedContent string))
PollAgentMessages polls the broker for agent_message control messages destined to the given runID every 2 seconds. Each received message is wrapped with channelwrap and passed to onMessage. The goroutine exits when ctx is done.
func (*Broker) PushControl ¶
func (b *Broker) PushControl(runID string, msg ControlMessage) error
PushControl queues a control message for the given run.
func (*Broker) Send ¶
func (b *Broker) Send(runID string, kind string, payload json.RawMessage, correlationID string) error
Send enqueues a control message for the given run using raw fields. It is a convenience wrapper around PushControl that builds the ControlMessage internally. correlationID is optional; it is used as the ControlMessage ID when non-empty.
func (*Broker) SendTo ¶
func (b *Broker) SendTo(fromRunID, toRunID, kind string, payload json.RawMessage, correlationID string) error
SendTo sends a message from one run to another run's control queue. The fromRunID must belong to a registered run whose token matches the authenticated caller's token.
func (*Broker) UpdateSessionInfo ¶ added in v0.28.1
func (b *Broker) UpdateSessionInfo(runID string, info *SessionInfo)
UpdateSessionInfo sets or updates the metadata for a registered run.
type ControlMessage ¶
type ControlMessage struct {
ID string `json:"id"`
Type string `json:"type"`
RunID string `json:"run_id"`
FromRunID string `json:"from_run_id,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
ControlMessage is a message pushed from Avenor to the sidecar for delivery to Claude.
func (ControlMessage) ToEnvelope ¶
func (m ControlMessage) ToEnvelope() Envelope
type Envelope ¶
type Envelope struct {
FromRunID string `json:"from_run_id"`
ToRunID string `json:"to_run_id"`
To string `json:"to,omitempty"`
Kind string `json:"kind"`
CorrelationID string `json:"correlation_id,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
Envelope is the internal generic message format used by the broker. It is not exposed on the wire; HTTP endpoints continue to use the existing Report/Finish/Reply/ControlMessage types.
type Finish ¶
type Finish struct {
RunID string `json:"run_id"`
Status string `json:"status"`
Summary string `json:"summary"`
FilesChanged []string `json:"files_changed,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
}
Finish is the payload from an avenor_finish tool call.
func (Finish) ToEnvelope ¶
type PermissionState ¶
type PermissionState struct {
RequestID string
ToolName string
Desc string
Preview string
CreatedAt time.Time
}
PermissionState holds a pending permission relay request.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder translates events.Event values into broker.Ingest calls so that any backend can mirror its event stream into the broker without an agent-side sidecar process.
Usage:
rec := broker.NewRecorder(brk, runID)
for evt := range provider.Events(ctx, sessionID) {
rec.Feed(evt)
subscriberCh <- evt // keep existing fanout
}
func NewRecorder ¶
NewRecorder creates a Recorder for the given broker and run ID.
func (*Recorder) Feed ¶
Feed translates an event into a broker.Ingest call. Events not listed below are silently dropped — the Recorder is a filtered mirror, not a complete event log.
session.end → kind = stop_reason (e.g. "done", "failed") — stored in Finishes agent.status → kind = "status" — stored in Reports agent.message_chunk → kind = "thinking" — stored in Reports permission.request → kind = "permission_requested" — stored in Reports child.question → kind = "child_question" — stored in Reports
Ingest errors (run not found, broker not started) are intentionally suppressed — the Recorder is best-effort and must not block or panic the event pump.
type Reply ¶
type Reply struct {
RunID string `json:"run_id"`
To string `json:"to"`
Payload json.RawMessage `json:"payload,omitempty"`
}
Reply is the payload from an avenor_reply tool call.
func (Reply) ToEnvelope ¶
type Report ¶
type Report struct {
RunID string `json:"run_id"`
State string `json:"state"`
Payload json.RawMessage `json:"payload,omitempty"`
}
Report is the payload from an avenor_report tool call.
func (Report) ToEnvelope ¶
type RunState ¶
type RunState struct {
RunID string
Token string
ControlQueue []*ControlMessage
RegisteredAt time.Time
LastSeen time.Time
Reports []Report
Finishes []Finish
Replies []Reply
PermissionRequests map[string]*PermissionState
PermissionDecisions map[string]string // requestID -> "allow" or "deny"
Mu sync.Mutex
Notify chan struct{}
// Ask/reply tracking
PendingAsks map[string]*AskEdge // messageID -> edge (sender is waiting)
WaitingReply map[string]chan AskReply // messageID -> buffered(1) channel for /wait_reply
// Session metadata for peer discovery
Info *SessionInfo
}
RunState holds all per-run broker state.
type SessionInfo ¶ added in v0.28.1
type SessionInfo struct {
RunID string `json:"run_id"`
Label string `json:"label,omitempty"`
Backend string `json:"backend,omitempty"`
Model string `json:"model,omitempty"`
Dir string `json:"dir,omitempty"`
Status string `json:"status,omitempty"`
LastSeen int64 `json:"last_seen"`
}
SessionInfo describes a registered run for peer discovery.