Documentation
¶
Overview ¶
Tool-activity events: the structured "what is the assistant doing right now" signal clients render while a turn is in flight.
They ride the protocol's existing shapes — a working-state TaskStatusUpdateEvent whose Status.Message carries a DataPart — rather than a custom channel, so a client that does not understand them simply sees the task working, and text streaming is untouched.
Tool arguments are the reason SummarizeToolInput exists: a tool's input is model-authored and may carry anything the user typed, so only a short, redacted, length-capped line ever leaves the service.
Granularity is per tool call (never per token) on purpose: the library records every status update against the stored task, so each event here is a task-store write.
Index ¶
- Constants
- Variables
- func BuildAgentCard(cfg *config.Config) *a2a.AgentCard
- func BuildExtendedAgentCard(cfg *config.Config, ents []capability.ServiceEntitlement) *a2a.AgentCard
- func BuildExtendedAgentCardFromSkills(cfg *config.Config, skills []a2a.AgentSkill) *a2a.AgentCard
- func Capabilities() a2a.AgentCapabilities
- func ProjectName(msg *a2a.Message, paramsMeta map[string]any) string
- func ServiceSkills(ents []capability.ServiceEntitlement) []a2a.AgentSkill
- func SummarizeToolInput(input json.RawMessage) string
- func UserText(msg *a2a.Message) string
- type AgentRunner
- type CardRequest
- type CompactRequest
- type Compactor
- type Executor
- type Mention
- type RunRequest
- type RunResult
- type RunSink
- type RunState
- type SkillAdvertiser
- type ToolActivity
Constants ¶
const AgentVersion = "0.1.0"
AgentVersion is the assistant's own version (AgentCard.version), distinct from the A2A protocol version. Mirrors the TS AGENT_VERSION.
const BearerSchemeName = "bearer"
BearerSchemeName is the security-scheme key advertised on the agent card.
Variables ¶
var ErrNothingToCompact = errors.New("a2a: nothing to compact")
ErrNothingToCompact is returned by Compactor.Compact when the target conversation has no history to fold — empty, or already reduced to a single summary turn by a prior compaction. It is a sentinel at this layer (distinct from, but mapped from, internal/agent's own ErrNothingToCompact) so this package and internal/server never need to import internal/agent just to recognize the case — the concrete AgentRunner implementation (cmd/assistant) does that mapping.
Functions ¶
func BuildAgentCard ¶
BuildAgentCard builds the A2A v1.0 AgentCard served at /.well-known/agent-card.json. UNSIGNED for v0 (no Signatures) — card signing is a documented follow-up. Ported from the TS buildAgentCard, retargeted to the a2a-go v1.0 card shape (SupportedInterfaces instead of the single url/preferredTransport pair).
func BuildExtendedAgentCard ¶
func BuildExtendedAgentCard(cfg *config.Config, ents []capability.ServiceEntitlement) *a2a.AgentCard
BuildExtendedAgentCard returns the AUTHENTICATED card for one project: the public card plus one skill per entitled provider service. Service names, tool names and MCP endpoints are safe here — the caller has been authorized for this project — but every value MUST come from that project's capability documents.
func BuildExtendedAgentCardFromSkills ¶
BuildExtendedAgentCardFromSkills shapes the extended card around skills an SkillAdvertiser already derived, so the server's card producer never re-implements card shaping. The generic project-assistant skill stays first: a project entitled to nothing still advertises something.
func Capabilities ¶
func Capabilities() a2a.AgentCapabilities
Capabilities is the single source of truth for what this agent supports. It feeds BOTH the published card and a2asrv.WithCapabilityChecks — the handler refuses GetExtendedAgentCard when its own copy says false, so the two must never drift.
func ProjectName ¶
ProjectName extracts the Milo project name from an A2A message's metadata, falling back to the request-level metadata. Returns "" when absent.
func ServiceSkills ¶
func ServiceSkills(ents []capability.ServiceEntitlement) []a2a.AgentSkill
ServiceSkills renders one A2A skill per entitled service. IDs come from capability.ServiceEntitlement.ID — the sanitized, de-duplicated serviceRef — rather than the raw provider-supplied name, so they are collision-free, stable across calls, and follow the same convention as the tool and skill names on the same card.
func SummarizeToolInput ¶
func SummarizeToolInput(input json.RawMessage) string
SummarizeToolInput renders a tool call's JSON arguments as one short line like `project=demo, region=us-east`, for display next to the tool's name.
It is deliberately lossy: only top-level scalars are shown (nested objects and arrays are elided), credential-shaped keys are redacted, values and the whole line are truncated. Anything it cannot parse yields "" rather than echoing raw input back to the client.
Types ¶
type AgentRunner ¶
type AgentRunner interface {
Run(ctx context.Context, req RunRequest, sink RunSink) RunResult
}
AgentRunner drives one conversation turn: it composes capabilities, runs the model/tool loop, emits usage events, streams incremental text via sink, and returns the terminal outcome. It is the seam between this A2A surface and the agent-orchestration layer (internal/agent); cancellation flows through ctx.
This interface is defined consumer-side so the orchestration package need not import this one; cmd/assistant adapts the concrete implementation to it.
type CardRequest ¶
type CardRequest struct {
ProjectName string
}
CardRequest identifies one authenticated extended-agent-card lookup.
ProjectName arrives on the A2A wire as GetExtendedAgentCardRequest.Tenant. NOTE the overload: A2A defines "tenant" as the ID of the agent owner, and internal/tenant uses "tenant" for the milo project scoping an apiserver read. Here it is neither — it is the milo PROJECT the caller is asking about, because GetExtendedAgentCardRequest carries no other field and the project is what scopes every capability in this service. The middleware authorizes the same field it is read from, so the two cannot disagree.
type CompactRequest ¶
type CompactRequest struct {
ProjectName string
// ContextID is the A2A contextId == conversation id whose history is
// being compacted.
ContextID string
}
CompactRequest identifies one manual, user-triggered history compaction (the "/compact" command), as opposed to a RunRequest turn.
type Compactor ¶
type Compactor interface {
Compact(ctx context.Context, req CompactRequest) error
}
Compactor is implemented by an AgentRunner that also supports manual history compaction. It is a separate, optional interface — not folded into AgentRunner — because not every runner needs to support it (fakes used in tests of the run path have no reason to).
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor implements a2asrv.AgentExecutor: it translates one agent run into the A2A v1.0 event sequence (submitted → working → artifact(s) → terminal status). The library owns everything else — task store, SSE framing, the JSON-RPC dispatch. Protocol validation of the message shape happens in the library; assistant-specific validation (projectName, non-empty text) happens here and surfaces as ErrInvalidParams.
func NewExecutor ¶
func NewExecutor(runner AgentRunner, logger *slog.Logger) *Executor
NewExecutor returns an Executor driving runner. A nil logger is silenced.
func (*Executor) Cancel ¶
func (e *Executor) Cancel(_ context.Context, ec *a2asrv.ExecutorContext) iter.Seq2[a2a.Event, error]
Cancel emits a canceled status-update. The library handles not-found and not-cancelable (terminal-state) cases before Cancel is ever reached.
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, ec *a2asrv.ExecutorContext) iter.Seq2[a2a.Event, error]
Execute runs one turn and emits its A2A events. Errors yielded before any event become JSON-RPC errors (e.g. ErrInvalidParams → -32602); once events flow, a failure is reported as a failed status-update, not an error.
type Mention ¶
type Mention struct {
Kind string `json:"kind"`
Name string `json:"name"`
APIGroup string `json:"apiGroup,omitempty"`
}
Mention is one resource the user referenced in their message. APIGroup is present when the client resolved it from API discovery and empty otherwise.
func Mentions ¶
Mentions extracts the referenced resources from an A2A message's metadata, falling back to the request-level metadata the way ProjectName does. Anything malformed or oversized yields nil rather than an error: a mention is a hint for the turn, never a reason to reject a message the user can see nothing wrong with.
type RunRequest ¶
type RunRequest struct {
UserText string
ProjectName string
// ContextID is the A2A contextId == conversation id == metering resource.
ContextID string
TaskID string
// Mentions are the resources the user pointed at with "@kind/name". They
// are advisory context for the turn, already present verbatim in UserText.
Mentions []Mention
}
RunRequest is one conversation turn to run for a task.
type RunResult ¶
type RunResult struct {
State RunState
// Text is the final assistant answer (accumulated across streamed deltas).
Text string
// Error is a human-readable failure message when State is [RunFailed].
Error string
}
RunResult is the terminal outcome of AgentRunner.Run.
type RunSink ¶
type RunSink interface {
// OnTextDelta is called for each chunk of generated assistant text.
OnTextDelta(text string)
// OnToolStart is called when the model asks for a tool (including the
// built-in load_skill, which is how a skill load becomes visible).
OnToolStart(act ToolActivity)
// OnToolFinish is called once the call has run, with its outcome and
// elapsed time.
OnToolFinish(act ToolActivity)
}
RunSink receives incremental output while an agent run is in progress. The executor's sink implementation translates each delta into an A2A artifact event and each tool lifecycle callback into a working status update.
type SkillAdvertiser ¶
type SkillAdvertiser interface {
ProjectSkills(ctx context.Context, req CardRequest) ([]a2a.AgentSkill, error)
}
SkillAdvertiser is implemented by an AgentRunner that can describe what a project is entitled to. Optional, exactly like Compactor: a nil advertiser means the extended card is simply not offered.
type ToolActivity ¶
type ToolActivity struct {
// ID correlates the started and finished halves of one call. Empty when
// the provider assigned no tool-call id.
ID string
// Name is the model-facing tool name (a provider tool, or a built-in such
// as load_skill).
Name string
// Summary is a short human-readable argument line, e.g. "project=demo".
Summary string
// OK reports success; meaningful on the finished half only.
OK bool
// Elapsed is how long the call took; meaningful on the finished half only.
Elapsed time.Duration
}
ToolActivity is one tool invocation as the client should see it: enough to render an activity row, and nothing else. Summary is already redacted and capped by SummarizeToolInput — raw tool arguments never reach this struct, because everything in it is streamed to the client.