Documentation
¶
Overview ¶
Package agent defines one agent declaratively: an identity, a capability card, and a step plan, bound into one value. It also holds the envelope-to-events translator, the composition-layer code that turns a delivered Message, an Ack, or a verified thread into one events.Event.
Map: agent.go = Agent, New, Name, Capabilities. events.go = MessageDeliveredEvent, MessageAckedEvent, ThreadVerifiedEvent. translator.go = ErrNoBus, EmitMessageDelivered, EmitMessageAcked, EmitThreadVerified. run.go = AckWait, Run, ErrEscalated, ErrNoWait, ErrNoThread, ErrOverBudget, Run's optional *heartbeat.Monitor beat and forget logic, Run's optional *contextbudget.Limits budget check, and confirmStep's room-stamping of Message.Room before a.id.Sign. The definition is data; it states who the agent is, what it can do, and what it runs. Run drives the bound plan in-process, through flow.Run. Rationale: ../docs/plans/agent.md. Contribution rules: ../AGENTS.md.
Index ¶
- Constants
- Variables
- func EmitMessageAcked(ctx context.Context, bus *events.Bus, a envelope.Ack) error
- func EmitMessageDelivered(ctx context.Context, bus *events.Bus, m envelope.Message) error
- func EmitThreadVerified(ctx context.Context, bus *events.Bus, msgs []envelope.Message) error
- type AckWait
- type Agent
Constants ¶
const MessageAckedEvent events.Name = "agent.message_acked"
MessageAckedEvent is the event kind EmitMessageAcked emits after an Ack validates. See translator.go.
const MessageDeliveredEvent events.Name = "agent.message_delivered"
MessageDeliveredEvent is the event kind EmitMessageDelivered emits after a delivered Message verifies. See translator.go.
const ThreadVerifiedEvent events.Name = "agent.thread_verified"
ThreadVerifiedEvent is the event kind EmitThreadVerified emits after a thread verifies. See translator.go.
Variables ¶
var ( ErrNoIdentity = errors.New("agent: identity is required") ErrNoPlan = errors.New("agent: plan is required") )
Sentinel errors for New and Run; test with errors.Is. A card validation failure wraps discovery's own error instead, because discovery exports no sentinel.
var ( ErrEscalated = errors.New("agent: step escalated") ErrNoWait = errors.New("agent: wait is required") ErrNoThread = errors.New("agent: thread id is required") ErrOverBudget = errors.New("agent: context budget exceeded") )
Sentinel errors for Run; test with errors.Is. ErrNoBus, already exported by the phase 20 translator, is reused for a nil bus.
var ErrNoBus = errors.New("agent: bus is required")
ErrNoBus is the sentinel every EmitX function returns when its bus argument is nil. It replaces a nil-pointer panic inside events.Bus.Emit; panics inside a package violate AGENTS.md.
Functions ¶
func EmitMessageAcked ¶
EmitMessageAcked validates a, then emits one Event named MessageAckedEvent onto bus. It returns ErrNoBus when bus is nil. It returns the Validate error, unwrapped, when a fails to validate; neither failure emits an event. On success it returns the raw error from bus.Emit.
func EmitMessageDelivered ¶
EmitMessageDelivered verifies m's signature, then emits one Event named MessageDeliveredEvent onto bus. It returns ErrNoBus when bus is nil. It returns the VerifySignature error, unwrapped, when m fails to verify; neither failure emits an event. On success it returns the raw error from bus.Emit.
func EmitThreadVerified ¶
EmitThreadVerified verifies msgs as one hash-linked thread, then emits one Event named ThreadVerifiedEvent onto bus. It returns ErrNoBus when bus is nil. It returns the VerifyThread error, unwrapped, when the thread fails to verify; neither failure emits an event. On success it returns the raw error from bus.Emit.
Types ¶
type AckWait ¶
AckWait resolves one step's ack. Run calls it once per step flow.Run gates behind Confirm, with the signed step message. It returns the receiver's real envelope.Ack, or an error. An implementation wraps ErrEscalated with %w to route the step to a human instead of resolving an ack.
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent binds one identity, one capability card, and one step plan into a single declarative value. Build it with New; the fields stay unexported.
func New ¶
New builds an Agent from an identity, a capability card, and a step plan. It checks id for nil, calls card.Validate(), then checks plan for nil, in that order, and returns the first error hit. New does not re-run flow's cycle check: a plan built through flow.New already passed it, and a zero-value plan carries no step for the check to reject.
func (*Agent) Capabilities ¶
Capabilities returns the card's Capabilities slice: the same backing array Parse or the caller set, with no defensive copy. This matches discovery.Card, which carries the same caller-owned mutability.
func (*Agent) Name ¶
Name returns the card's Name field, unchanged. It applies no trim; Card.Validate already rejects a name that is blank after TrimSpace, and Name returns the stored value as-is.
func (*Agent) Plan ¶
func (a *Agent) Plan() *flow.Definition
Plan returns the step plan New bound to a. The returned pointer is the same one New stored; read it through flow's accessors and never mutate it.
func (*Agent) Run ¶
func (a *Agent) Run( ctx context.Context, threadID string, m *machine.Definition, in machine.InOut, wait AckWait, bus *events.Bus, hb *heartbeat.Monitor, room string, budget *contextbudget.Limits, ) (machine.Status, machine.InOut, error)
Run drives a's bound plan (the *flow.Definition New bound) through flow.Run, in-process. threadID names the one envelope thread this run's step messages share. m is the status model the plan's steps target. in is the starting record. wait resolves each gated step's ack. bus receives MessageDeliveredEvent, MessageAckedEvent, and, once per successful run with one or more gated steps, ThreadVerifiedEvent.
Run checks a and a.id for nil first, then wait for nil, then bus for nil, then threadID for empty, in that order, before it touches m or a's plan; each check returns machine.Status(""), in unchanged, and its sentinel.
For each step flow.Run gates behind Confirm, Run builds an envelope.Message from the step's ID, threadID, and Payload, with Version, Intent, and Epistemic set to values that pass Validate on their own, signs it with a's identity, and chains it to the previous step message with PrevHash. It calls EmitMessageDelivered, then wait. A wait error returns unchanged, without calling EmitMessageAcked. A nil wait error runs EmitMessageAcked and requires AckConfirmed before the step counts as done.
A step confirmed more than once in one thread keeps the thread's IDs unique: the second message gets "#2" appended to the step ID, the third "#3", and so on. A looped child step and two Sub children sharing one step ID both hit this rule. The suffix never collides with a caller step ID: the scan skips any suffix already in use.
On a successful run with one or more gated steps, Run calls EmitThreadVerified once, over every step message it built, in order.
hb is an optional step-liveness heartbeat. A nil hb skips every heartbeat call; Run's behavior is otherwise unchanged. A non-nil hb beats one id, a.id.Signer()+":"+threadID, right before each gated step's wait call, and forgets that id once, on every return path. A panel step reaches no beat call; see docs/plans/agents/ phase26_agent_heartbeat.md's disclosed scope limit. Run never calls hb.Dead and never aborts a step on staleness; an external caller holding the same hb polls Dead on its own schedule.
An empty room reproduces today's zero-value behavior: Message.Room stays "". A non-empty room makes confirmStep stamp it onto Message.Room before a.id.Sign runs, on every gated step's built message.
budget is an optional context budget. A nil budget skips every budget check; Run's behavior is otherwise unchanged. A non-nil budget runs budget.Validate() once, at the same point Run checks wait, bus, and threadID; an invalid budget returns machine.Status(""), in unchanged, and the wrapped Validate error. A non-nil, valid budget makes confirmStep check budget.Fits, right before each gated step's wait call, against the cumulative byte total of every message built so far plus the step about to run, and the 1-indexed count of steps built so far including that step. A Fits failure returns ErrOverBudget, wrapping the step ID, without calling hb.Beat, wait, or EmitMessageAcked for that step. A panel step reaches no confirmStep wait call, so its payload never adds to the running total and never trips budget; see docs/plans/agents/ phase32_context_budget.md's disclosed scope limit.