Documentation
¶
Overview ¶
Package a2a implements the server side of the Agent2Agent protocol v1.0 task exchange over its JSON-RPC binding: SendMessage, SendStreamingMessage, GetTask, ListTasks, CancelTask, SubscribeToTask, the four push-notification-config operations, and GetExtendedAgentCard.
Wire facts this package pins, each verified against the canonical a2a.proto and the A2A project's own Go SDK rather than recalled:
- JSON-RPC method names are the PascalCase RPC names ("SendMessage", "GetTask", …). The v0.x slash forms ("message/send") are gone.
- Field names are camelCase; enums serialize as their proto names ("TASK_STATE_COMPLETED", "ROLE_USER"); timestamps are RFC 3339 UTC.
- A Part is a flat object carrying exactly one of text / raw / url / data plus optional filename, mediaType, metadata.
- A streaming result is a StreamResponse: an object with exactly one of task / message / statusUpdate / artifactUpdate.
- Streaming methods answer with text/event-stream; each event's data is a complete JSON-RPC response object whose result is a StreamResponse. The stream closes once the task is terminal or interrupted.
- Push notifications POST a StreamResponse to the configured URL with the A2A-Notification-Token header carrying the config's token.
The agent card (discovery) lives in framework/uihost; this package is the exchange behind the card's JSONRPC interface. Skills are what the server does with a message: a GoFastr app is a deterministic agent, so a skill is invoked by name (Message.metadata["skill"], or a data part carrying a "skill" key), never inferred from prose. See framework/docs/ content/a2a.md.
Index ¶
- Constants
- Variables
- func DefaultRouter(_ context.Context, msg *Message, skills []Skill) (string, error)
- func RequestFromContext(ctx context.Context) (*http.Request, bool)
- func WithRequest(ctx context.Context, r *http.Request) context.Context
- type Artifact
- type AuthenticationInfo
- type CancelTaskRequest
- type Capabilities
- type Config
- type DeleteTaskPushNotificationConfigRequest
- type Error
- type GetExtendedAgentCardRequest
- type GetTaskPushNotificationConfigRequest
- type GetTaskRequest
- type Handler
- type ListQuery
- type ListTaskPushNotificationConfigsRequest
- type ListTaskPushNotificationConfigsResponse
- type ListTasksRequest
- type ListTasksResponse
- type MemoryStore
- func (m *MemoryStore) CreatePushConfig(_ context.Context, rec *PushConfigRecord) error
- func (m *MemoryStore) CreateTask(_ context.Context, rec *TaskRecord) error
- func (m *MemoryStore) DeletePushConfig(_ context.Context, owner, taskID, id string) error
- func (m *MemoryStore) GetPushConfig(_ context.Context, owner, taskID, id string) (*PushConfigRecord, error)
- func (m *MemoryStore) GetTask(_ context.Context, owner, id string) (*TaskRecord, error)
- func (m *MemoryStore) ListPushConfigs(_ context.Context, owner, taskID string) ([]*PushConfigRecord, error)
- func (m *MemoryStore) ListTasks(_ context.Context, owner string, q ListQuery) (recs []*TaskRecord, total int, err error)
- func (m *MemoryStore) TrimPushConfigs(_ context.Context, owner, taskID string, keep int) error
- func (m *MemoryStore) TrimTerminalTasks(_ context.Context, owner string, keep int) error
- func (m *MemoryStore) UpdateTask(_ context.Context, rec *TaskRecord) error
- type Message
- type Part
- type PushConfigRecord
- type PushNotificationConfig
- type PushOptions
- type RetentionTrimmer
- type Role
- type Router
- type SQLStore
- func (s *SQLStore) CreatePushConfig(ctx context.Context, rec *PushConfigRecord) error
- func (s *SQLStore) CreateTask(ctx context.Context, rec *TaskRecord) error
- func (s *SQLStore) DeletePushConfig(ctx context.Context, owner, taskID, id string) error
- func (s *SQLStore) GetPushConfig(ctx context.Context, owner, taskID, id string) (*PushConfigRecord, error)
- func (s *SQLStore) GetTask(ctx context.Context, owner, id string) (*TaskRecord, error)
- func (s *SQLStore) ListPushConfigs(ctx context.Context, owner, taskID string) ([]*PushConfigRecord, error)
- func (s *SQLStore) ListTasks(ctx context.Context, owner string, q ListQuery) ([]*TaskRecord, int, error)
- func (s *SQLStore) TrimPushConfigs(ctx context.Context, owner, taskID string, keep int) error
- func (s *SQLStore) TrimTerminalTasks(ctx context.Context, owner string, keep int) error
- func (s *SQLStore) UpdateTask(ctx context.Context, rec *TaskRecord) error
- type SQLStoreOption
- type SendMessageConfiguration
- type SendMessageRequest
- type SendMessageResponse
- type Server
- type Skill
- type Store
- type StreamResponse
- type SubscribeToTaskRequest
- type Task
- type TaskArtifactUpdateEvent
- type TaskContext
- type TaskRecord
- type TaskState
- type TaskStatus
- type TaskStatusUpdateEvent
- type Timestamp
Constants ¶
const ( CodeParseError = -32700 CodeInvalidRequest = -32600 CodeMethodNotFound = -32601 CodeInvalidParams = -32602 CodeInternalError = -32603 CodeTaskNotFound = -32001 CodeTaskNotCancelable = -32002 CodePushNotificationNotSupported = -32003 CodeUnsupportedOperation = -32004 CodeContentTypeNotSupported = -32005 CodeInvalidAgentResponse = -32006 CodeExtendedAgentCardNotConfigured = -32007 CodeExtensionSupportRequired = -32008 CodeVersionNotSupported = -32009 CodeUnauthenticated = -31401 )
JSON-RPC error codes. The -327xx/-326xx block is JSON-RPC 2.0 itself; the -3200x block is the A2A v1.0 error mapping (spec §5.4); -31401/-31403 are the auth codes the A2A project's SDKs use for a caller the server could not identify or would not allow, kept so a client built on those SDKs maps our refusals to the same errors it already handles.
const ( MethodSendMessage = "SendMessage" MethodSendStreamingMessage = "SendStreamingMessage" MethodGetTask = "GetTask" MethodListTasks = "ListTasks" MethodCancelTask = "CancelTask" MethodSubscribeToTask = "SubscribeToTask" MethodCreateTaskPushNotificationConfig = "CreateTaskPushNotificationConfig" MethodGetTaskPushNotificationConfig = "GetTaskPushNotificationConfig" MethodListTaskPushNotificationConfigs = "ListTaskPushNotificationConfigs" MethodDeleteTaskPushNotificationConfig = "DeleteTaskPushNotificationConfig" MethodGetExtendedAgentCard = "GetExtendedAgentCard" )
JSON-RPC method names of the A2A v1.0 JSON-RPC binding. Pinned as the PascalCase RPC names from specification/a2a.proto (§5.3 method mapping); the v0.x slash forms are wire-incompatible, not merely outdated.
const ProtocolVersion = "1.0"
ProtocolVersion is the A2A protocol version this package speaks, in the Major.Minor form the agent card's supportedInterfaces expects.
const PushNotificationTokenHeader = "A2A-Notification-Token" // not-a-secret: a header name
PushNotificationTokenHeader is the header carrying PushNotificationConfig.Token on a push delivery, so the receiver can check the notification came from the agent it registered with.
Variables ¶
var ( ErrNotFound = errors.New("a2a: not found") ErrConflict = errors.New("a2a: version conflict") )
Store errors. Implementations return these (or wrap them) so the server can map them to JSON-RPC codes without knowing the backend.
var Methods = []string{ MethodSendMessage, MethodSendStreamingMessage, MethodGetTask, MethodListTasks, MethodCancelTask, MethodSubscribeToTask, MethodCreateTaskPushNotificationConfig, MethodGetTaskPushNotificationConfig, MethodListTaskPushNotificationConfigs, MethodDeleteTaskPushNotificationConfig, MethodGetExtendedAgentCard, }
Methods lists every JSON-RPC method the server dispatches, in spec order.
Functions ¶
func DefaultRouter ¶
DefaultRouter picks the skill for a message:
- msg.Metadata["skill"], when a non-empty string;
- else the first data part whose Data is a JSON object with a string "skill" key;
- else the only registered skill, when exactly one is;
- else an error naming the available ids.
A skill id that is not registered is an error at step 1 and 2 alike — the router names what exists rather than guessing.
func RequestFromContext ¶
RequestFromContext returns the request stashed by WithRequest.
Types ¶
type Artifact ¶
type Artifact struct {
ArtifactID string `json:"artifactId"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Parts []Part `json:"parts"`
Metadata map[string]any `json:"metadata,omitempty"`
Extensions []string `json:"extensions,omitempty"`
}
Artifact is an output produced by a task.
type AuthenticationInfo ¶
type AuthenticationInfo struct {
Scheme string `json:"scheme"`
Credentials string `json:"credentials,omitempty"`
}
AuthenticationInfo tells the agent how to authenticate to a push URL. Scheme "Bearer" or "Basic" become an Authorization header built from Credentials verbatim; other schemes are stored and ignored on send.
type CancelTaskRequest ¶
type CancelTaskRequest struct {
Tenant string `json:"tenant,omitempty"`
ID string `json:"id"`
Metadata map[string]any `json:"metadata,omitempty"`
}
CancelTaskRequest is the params object of CancelTask.
type Capabilities ¶
type Capabilities struct {
Streaming bool `json:"streaming"`
PushNotifications bool `json:"pushNotifications"`
ExtendedAgentCard bool `json:"extendedAgentCard,omitempty"`
}
Capabilities is what the server advertises; the agent card copies it.
type Config ¶
type Config struct {
// Skills is what the agent can do; at least one, unique ids, and
// every Handler non-nil.
Skills []Skill
// Store persists tasks and push configs. Nil → NewMemoryStore().
Store Store
// Router picks the skill for a message. Nil → DefaultRouter.
Router Router
// Owner resolves the caller for every request. ok=false → HTTP 401
// and CodeUnauthenticated. Nil is a NewServer error.
Owner func(r *http.Request) (owner string, ok bool)
// ExtendedCard, when set, serves GetExtendedAgentCard. Nil → the
// method answers CodeExtendedAgentCardNotConfigured and
// Capabilities().ExtendedAgentCard is false.
ExtendedCard func(ctx context.Context, owner string) (map[string]any, error)
// Push tunes push-notification delivery; see PushOptions.
Push PushOptions
// Logger for handler failures, drops, and push errors. Nil →
// slog.Default().
Logger *slog.Logger
// AllowedOrigins permits browser Origins that are not same-origin
// with the request: tunnels (ngrok, Codespaces) and split-origin
// web clients. The default posture refuses a present Origin naming
// any authority other than the request's own Host, mirroring the
// MCP transport's cross-origin gate; native A2A clients send no
// Origin and always pass.
AllowedOrigins []string
// MaxBodyBytes caps one request body. Default 1 MiB.
MaxBodyBytes int64
// TaskTimeout is the ceiling on one skill-handler run. Default
// 5 minutes.
TaskTimeout time.Duration
// MaxHistory caps stored history per task (oldest dropped).
// Default 200 messages.
MaxHistory int
// DefaultPageSize and MaxPageSize bound ListTasks paging.
// Defaults 50 / 200.
DefaultPageSize, MaxPageSize int
// TerminalTaskRetention bounds how many TERMINAL task rows
// (completed / failed / canceled / rejected) one owner retains:
// past the bound the OLDEST terminal rows are deleted after each
// settle. 0 = 64 (every row carries the caller's message body, and
// the default store is in-RAM, so cheap authenticated writes must
// not grow it without end); negative disables trimming for hosts
// that own retention themselves. Applies to stores implementing
// RetentionTrimmer — both built-ins do.
TerminalTaskRetention int
// MaxPushConfigsPerTask bounds push-notification configs per task
// (each one fans a delivery goroutine out per event): past the
// bound the OLDEST configs are deleted. 0 = 8; negative disables.
MaxPushConfigsPerTask int
// MaxStreamSeatsPerOwner bounds the concurrent SSE streams
// (SubscribeToTask + SendStreamingMessage) ONE owner may hold: each
// pins a relay goroutine, a bus channel, and tickers for the
// stream's whole life. 0 = 16 (the default every stream surface in
// the tree shares); negative lifts the cap for deployments that
// bound streams elsewhere.
MaxStreamSeatsPerOwner int
// MaxConcurrentRunsPerOwner bounds the concurrent skill-handler runs
// one owner may start, including ReturnImmediately runs. 0 = 16;
// negative disables the cap for deployments that bound runs elsewhere.
MaxConcurrentRunsPerOwner int
// StreamSeatOverflow selects what an owner at MaxStreamSeatsPerOwner
// does with their next stream: stream.SeatOverflowRefuse (the
// default) answers 429 at connect; stream.SeatOverflowEvictOldest
// closes that owner's oldest stream and seats the new one — the
// multi-tab-friendly policy for clients that reconnect faster than
// a half-open connection's seat is reclaimed.
StreamSeatOverflow stream.SeatOverflowPolicy
}
Config constructs a Server. Owner is required: multi-user scoping is not optional, so a nil Owner is a NewServer error rather than a silently single-tenant server.
type DeleteTaskPushNotificationConfigRequest ¶
type DeleteTaskPushNotificationConfigRequest struct {
Tenant string `json:"tenant,omitempty"`
TaskID string `json:"taskId"`
ID string `json:"id"`
}
DeleteTaskPushNotificationConfigRequest is the params object of DeleteTaskPushNotificationConfig. The result is an empty object.
type Error ¶
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
Error is a JSON-RPC error object. Returning one from a skill handler or a Store surfaces it to the client with its code intact; any other error is reported as CodeInternalError with a generic message, never its text.
func ErrTaskNotCancelable ¶
ErrTaskNotCancelable is the refusal for cancelling a task already in a terminal state.
func ErrTaskNotFound ¶
ErrTaskNotFound is the refusal for an unknown task id — or, deliberately, for a task another owner holds: the two are indistinguishable to the caller, so task ids do not enumerate across owners.
func ErrUnauthenticated ¶
func ErrUnauthenticated() *Error
ErrUnauthenticated is the refusal for a caller with no resolved identity.
func ErrUnsupportedOperation ¶
ErrUnsupportedOperation is the refusal for an operation the task's state does not admit, such as a message to a terminal task.
type GetExtendedAgentCardRequest ¶
type GetExtendedAgentCardRequest struct {
Tenant string `json:"tenant,omitempty"`
}
GetExtendedAgentCardRequest is the params object of GetExtendedAgentCard.
type GetTaskPushNotificationConfigRequest ¶
type GetTaskPushNotificationConfigRequest struct {
Tenant string `json:"tenant,omitempty"`
TaskID string `json:"taskId"`
ID string `json:"id"`
}
GetTaskPushNotificationConfigRequest is the params object of GetTaskPushNotificationConfig.
type GetTaskRequest ¶
type GetTaskRequest struct {
Tenant string `json:"tenant,omitempty"`
ID string `json:"id"`
HistoryLength *int `json:"historyLength,omitempty"`
}
GetTaskRequest is the params object of GetTask.
type Handler ¶
type Handler func(ctx context.Context, t TaskContext) error
Handler does the work of a skill for one inbound message. It reports progress and results through the TaskContext; returning nil with the task still in progress completes it, returning an error fails it (a context cancellation cancels it instead). A returned *Error keeps its code and message; any other error is logged and the client sees the generic "skill handler failed", so a wrapped database or network error never reaches a caller. Fail(TextPart(...)) is the way to say why.
type ListQuery ¶
type ListQuery struct {
ContextID string
Status TaskState
// After filters to tasks whose status timestamp is after this instant
// (zero: no filter).
After time.Time
Limit int
Offset int
}
ListQuery is the store-level shape of ListTasks.
type ListTaskPushNotificationConfigsRequest ¶
type ListTaskPushNotificationConfigsRequest struct {
Tenant string `json:"tenant,omitempty"`
TaskID string `json:"taskId"`
PageSize *int `json:"pageSize,omitempty"`
PageToken string `json:"pageToken,omitempty"`
}
ListTaskPushNotificationConfigsRequest is the params object of ListTaskPushNotificationConfigs.
type ListTaskPushNotificationConfigsResponse ¶
type ListTaskPushNotificationConfigsResponse struct {
Configs []PushNotificationConfig `json:"configs"`
NextPageToken string `json:"nextPageToken,omitempty"`
}
ListTaskPushNotificationConfigsResponse is the result of ListTaskPushNotificationConfigs. Configs is never null on the wire.
type ListTasksRequest ¶
type ListTasksRequest struct {
Tenant string `json:"tenant,omitempty"`
ContextID string `json:"contextId,omitempty"`
Status TaskState `json:"status,omitempty"`
PageSize *int `json:"pageSize,omitempty"`
PageToken string `json:"pageToken,omitempty"`
HistoryLength *int `json:"historyLength,omitempty"`
StatusTimestampAfter *Timestamp `json:"statusTimestampAfter,omitempty"`
IncludeArtifacts *bool `json:"includeArtifacts,omitempty"`
}
ListTasksRequest is the params object of ListTasks.
type ListTasksResponse ¶
type ListTasksResponse struct {
Tasks []Task `json:"tasks"`
NextPageToken string `json:"nextPageToken"`
PageSize int `json:"pageSize"`
TotalSize int `json:"totalSize"`
}
ListTasksResponse is the result of ListTasks. Tasks is never null on the wire: an empty page encodes as [].
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is the in-process Store: a mutex, two maps, deep copies at the boundary. Every read and write is scoped by owner in the map key, not filtered after the fact, so the owner predicate cannot be forgotten on a new query shape. Use the SQLStore to share tasks across replicas.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore returns an empty in-memory store.
func (*MemoryStore) CreatePushConfig ¶
func (m *MemoryStore) CreatePushConfig(_ context.Context, rec *PushConfigRecord) error
func (*MemoryStore) CreateTask ¶
func (m *MemoryStore) CreateTask(_ context.Context, rec *TaskRecord) error
func (*MemoryStore) DeletePushConfig ¶
func (m *MemoryStore) DeletePushConfig(_ context.Context, owner, taskID, id string) error
func (*MemoryStore) GetPushConfig ¶
func (m *MemoryStore) GetPushConfig(_ context.Context, owner, taskID, id string) (*PushConfigRecord, error)
func (*MemoryStore) GetTask ¶
func (m *MemoryStore) GetTask(_ context.Context, owner, id string) (*TaskRecord, error)
func (*MemoryStore) ListPushConfigs ¶
func (m *MemoryStore) ListPushConfigs(_ context.Context, owner, taskID string) ([]*PushConfigRecord, error)
func (*MemoryStore) ListTasks ¶
func (m *MemoryStore) ListTasks(_ context.Context, owner string, q ListQuery) (recs []*TaskRecord, total int, err error)
func (*MemoryStore) TrimPushConfigs ¶ added in v0.85.0
TrimPushConfigs implements RetentionTrimmer: it keeps the newest `keep` configs for (owner, taskID) — the same order ListPushConfigs serves — and deletes the older ones.
func (*MemoryStore) TrimTerminalTasks ¶ added in v0.85.0
TrimTerminalTasks implements RetentionTrimmer: it keeps the newest `keep` terminal rows for owner and deletes the older ones. Non-terminal rows (a task in flight, or paused on input) are never retention's to touch.
func (*MemoryStore) UpdateTask ¶
func (m *MemoryStore) UpdateTask(_ context.Context, rec *TaskRecord) error
type Message ¶
type Message struct {
MessageID string `json:"messageId"`
ContextID string `json:"contextId,omitempty"`
TaskID string `json:"taskId,omitempty"`
Role Role `json:"role"`
Parts []Part `json:"parts"`
Metadata map[string]any `json:"metadata,omitempty"`
Extensions []string `json:"extensions,omitempty"`
ReferenceTaskIDs []string `json:"referenceTaskIds,omitempty"`
}
Message is one turn of the exchange, from the client (RoleUser) or the agent (RoleAgent).
type Part ¶
type Part struct {
Text *string `json:"text,omitempty"`
Raw []byte `json:"raw,omitempty"`
URL *string `json:"url,omitempty"`
Data *any `json:"data,omitempty"`
Filename string `json:"filename,omitempty"`
MediaType string `json:"mediaType,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
Part is one unit of content in a message or artifact. Exactly one of Text, Raw, URL, or Data is set; the JSON form is flat, with the set field as the discriminator: {"text": "…"}, {"raw": "<base64>"}, {"url": "…"}, {"data": <any JSON value>}. Filename, MediaType and Metadata are optional on every kind.
Data is a pointer so that a JSON null or false or 0 data payload is distinguishable from "no data part": the pointer is non-nil whenever the key was present.
func DataPart ¶
DataPart builds a structured-data part. MediaType defaults to application/json at the caller's discretion; the spec leaves it optional.
func (Part) Kind ¶
Kind returns which content field the part carries: "text", "raw", "url", "data", or "" when none is set (an invalid part).
func (*Part) UnmarshalJSON ¶
UnmarshalJSON decodes a part and enforces the exactly-one-content rule, so a malformed client part is refused at the boundary rather than reaching a skill as an empty Part.
type PushConfigRecord ¶
type PushConfigRecord struct {
Config PushNotificationConfig
Owner string
CreatedAt time.Time
}
PushConfigRecord is a push-notification config as the Store holds it.
type PushNotificationConfig ¶
type PushNotificationConfig struct {
Tenant string `json:"tenant,omitempty"`
ID string `json:"id,omitempty"`
TaskID string `json:"taskId"`
URL string `json:"url"`
Token string `json:"token,omitempty"`
Authentication *AuthenticationInfo `json:"authentication,omitempty"`
}
PushNotificationConfig is a webhook the agent notifies about one task. It is both the params object of CreateTaskPushNotificationConfig and the result of the create/get calls.
type PushOptions ¶
type PushOptions struct {
// Client delivers notifications. Nil means a 10s-timeout client on
// the SSRF-guarded transport. A supplied client is used as a
// template: redirect following is forced off (a 3xx must never carry
// the notification token to a second host) and, unless AllowPrivate
// is set, it is wrapped with the dial-time guard, so a caller cannot
// accidentally opt registered URLs out of the SSRF posture by
// bringing their own client.
Client *http.Client
// AllowPrivate permits internal hosts (loopback, RFC1918, CGNAT,
// link-local). Tests and internal deployments only: a
// caller-registered URL that the server POSTs to is an SSRF vector
// otherwise.
AllowPrivate bool
// Disable turns the capability off: Capabilities().PushNotifications
// reports false, CreateTaskPushNotificationConfig answers
// CodePushNotificationNotSupported, and a config inside
// SendMessage's configuration is refused the same way.
Disable bool
}
PushOptions tunes push-notification delivery. The zero value delivers over a guarded default client with private hosts refused.
type RetentionTrimmer ¶ added in v0.85.0
type RetentionTrimmer interface {
// TrimTerminalTasks deletes the owner's OLDEST terminal task rows
// until at most keep remain. Non-terminal rows are never touched.
// keep < 0 means unlimited (no-op).
TrimTerminalTasks(ctx context.Context, owner string, keep int) error
// TrimPushConfigs deletes the OLDEST push-notification configs
// registered for (owner, taskID) until at most keep remain.
// keep < 0 means unlimited (no-op).
TrimPushConfigs(ctx context.Context, owner, taskID string, keep int) error
}
RetentionTrimmer is the optional Store capability that bounds the rows cheap authenticated writes mint. The server asks the store to trim after every persist that leaves a task terminal and after every push-config create; a store that does not implement it keeps its own retention policy. Both built-in stores implement it.
type Role ¶
type Role string
Role is the author of a message; serializes as "ROLE_USER" / "ROLE_AGENT".
type Router ¶
Router picks the skill for a message. The default reads msg.Metadata["skill"], then the first data part carrying a "skill" key, then the only skill when exactly one is registered, and otherwise rejects the task with a message naming the available skill ids.
type SQLStore ¶
type SQLStore struct {
// contains filtered or unexported fields
}
SQLStore persists tasks and push configs in an existing *sql.DB (SQLite or Postgres, probed like battery/queue's detectDBDialect). The wire Task lives in a task_json column; the scalar columns exist only for owner scoping, filtering, and paging, so the JSON shape can evolve without a migration for every field. Construct with NewSQLStore; tables are created IF NOT EXISTS. Safe for concurrent use and shared across replicas.
func NewSQLStore ¶
func NewSQLStore(db *sql.DB, opts ...SQLStoreOption) (*SQLStore, error)
NewSQLStore constructs the store and ensures its tables exist.
func (*SQLStore) CreatePushConfig ¶
func (s *SQLStore) CreatePushConfig(ctx context.Context, rec *PushConfigRecord) error
func (*SQLStore) CreateTask ¶
func (s *SQLStore) CreateTask(ctx context.Context, rec *TaskRecord) error
func (*SQLStore) DeletePushConfig ¶
func (*SQLStore) GetPushConfig ¶
func (*SQLStore) ListPushConfigs ¶
func (*SQLStore) TrimPushConfigs ¶ added in v0.85.0
TrimPushConfigs implements RetentionTrimmer: it keeps the newest `keep` configs for (owner, taskID) — the order ListPushConfigs serves — and deletes the older ones.
func (*SQLStore) TrimTerminalTasks ¶ added in v0.85.0
TrimTerminalTasks implements RetentionTrimmer: it keeps the newest `keep` terminal rows for owner and deletes the older ones. The id walk then delete (instead of one DELETE ... LIMIT statement) keeps both dialects happy; ids are deleted in bounded batches.
func (*SQLStore) UpdateTask ¶
func (s *SQLStore) UpdateTask(ctx context.Context, rec *TaskRecord) error
UpdateTask persists rec only when its Version matches the stored row; on success rec.Version is the new stored version and UpdatedAt is refreshed. Zero rows updated means conflict or absence, distinguished by one follow-up select on that path only.
type SQLStoreOption ¶
type SQLStoreOption func(*SQLStore)
SQLStoreOption configures NewSQLStore.
func WithTablePrefix ¶
func WithTablePrefix(prefix string) SQLStoreOption
WithTablePrefix puts both tables behind a prefix (e.g. "tenant1_"), for schemas shared by several services.
type SendMessageConfiguration ¶
type SendMessageConfiguration struct {
AcceptedOutputModes []string `json:"acceptedOutputModes,omitempty"`
TaskPushNotificationConfig *PushNotificationConfig `json:"taskPushNotificationConfig,omitempty"`
HistoryLength *int `json:"historyLength,omitempty"`
ReturnImmediately bool `json:"returnImmediately,omitempty"`
}
SendMessageConfiguration tunes one SendMessage call.
type SendMessageRequest ¶
type SendMessageRequest struct {
Tenant string `json:"tenant,omitempty"`
Message *Message `json:"message"`
Configuration *SendMessageConfiguration `json:"configuration,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
SendMessageRequest is the params object of SendMessage and SendStreamingMessage.
type SendMessageResponse ¶
type SendMessageResponse struct {
Task *Task `json:"task,omitempty"`
Message *Message `json:"message,omitempty"`
}
SendMessageResponse is the non-streaming result: a task (the usual outcome) or, for an agent that answers without a task, a message.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the A2A task-exchange HTTP handler. Construct with NewServer and mount behind the middleware that establishes the principal Config.Owner resolves.
func (*Server) Capabilities ¶
func (s *Server) Capabilities() Capabilities
Capabilities is what the agent card copies: this server always streams, and push/extended card follow configuration.
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP answers one JSON-RPC request. POST only; the caller's Origin must be absent or name the request's own authority, the body is capped, the content type must be JSON, and the caller must resolve to an owner before any method runs.
func (*Server) SetAllowedOrigins ¶ added in v0.83.0
SetAllowedOrigins permits browser Origins that are not same-origin with the request, for servers constructed without going through Config.AllowedOrigins. Mirrors core/mcp's SetAllowedOrigins.
type Skill ¶
type Skill struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Tags []string `json:"tags"`
Examples []string `json:"examples,omitempty"`
InputModes []string `json:"inputModes,omitempty"`
OutputModes []string `json:"outputModes,omitempty"`
// Handler runs the skill for one message. Nil is a build error.
Handler Handler `json:"-"`
}
Skill is one thing the agent can do, invoked by name. The descriptive fields are exactly the AgentSkill fields of the agent card (spec §8), so the card and the server list one set of skills from one source.
type Store ¶
type Store interface {
CreateTask(ctx context.Context, rec *TaskRecord) error
// GetTask returns ErrNotFound for an unknown id OR another owner's task.
GetTask(ctx context.Context, owner, id string) (*TaskRecord, error)
// UpdateTask persists rec if rec.Version matches the stored row, then
// increments rec.Version; otherwise ErrConflict.
UpdateTask(ctx context.Context, rec *TaskRecord) error
// ListTasks returns the owner's tasks matching q, newest status first,
// plus the total match count before Limit/Offset.
ListTasks(ctx context.Context, owner string, q ListQuery) (recs []*TaskRecord, total int, err error)
CreatePushConfig(ctx context.Context, rec *PushConfigRecord) error
GetPushConfig(ctx context.Context, owner, taskID, id string) (*PushConfigRecord, error)
ListPushConfigs(ctx context.Context, owner, taskID string) ([]*PushConfigRecord, error)
DeletePushConfig(ctx context.Context, owner, taskID, id string) error
}
Store persists tasks and push configs. Every read and write is scoped by owner: a task belongs to the principal that created it, and no call returns or touches another owner's rows. Implementations are safe for concurrent use and shared across replicas (the SQL store); the memory store is per process.
type StreamResponse ¶
type StreamResponse struct {
Task *Task `json:"task,omitempty"`
Message *Message `json:"message,omitempty"`
StatusUpdate *TaskStatusUpdateEvent `json:"statusUpdate,omitempty"`
ArtifactUpdate *TaskArtifactUpdateEvent `json:"artifactUpdate,omitempty"`
}
StreamResponse is the streaming result and push-notification payload: an object carrying exactly one of task / message / statusUpdate / artifactUpdate. Construct with one field set; Validate rejects the rest.
func (StreamResponse) Validate ¶
func (r StreamResponse) Validate() error
Validate enforces the exactly-one rule for StreamResponse.
type SubscribeToTaskRequest ¶
type SubscribeToTaskRequest struct {
Tenant string `json:"tenant,omitempty"`
ID string `json:"id"`
}
SubscribeToTaskRequest is the params object of SubscribeToTask.
type Task ¶
type Task struct {
ID string `json:"id"`
ContextID string `json:"contextId"`
Status TaskStatus `json:"status"`
Artifacts []Artifact `json:"artifacts,omitempty"`
History []Message `json:"history,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
Task is the unit of work a client and an agent exchange messages about.
type TaskArtifactUpdateEvent ¶
type TaskArtifactUpdateEvent struct {
TaskID string `json:"taskId"`
ContextID string `json:"contextId"`
Artifact Artifact `json:"artifact"`
Append bool `json:"append,omitempty"`
LastChunk bool `json:"lastChunk,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
TaskArtifactUpdateEvent announces a new or extended artifact.
type TaskContext ¶
type TaskContext interface {
// Task returns a snapshot of the task as currently persisted.
Task() *Task
// Message returns the message this run was started or resumed with.
Message() *Message
// Owner returns the principal that owns the task (Config.Owner's
// value for the request that created it).
Owner() string
// Request returns the HTTP request that started or resumed this run,
// for handlers that re-dispatch into the app with the caller's
// credentials. Its Context is not the handler's context.
Request() *http.Request
// Working moves the task to TASK_STATE_WORKING with an optional agent
// status message (nil parts: no message). Idempotent once working.
Working(parts ...Part) error
// Artifact records an artifact and emits an artifact-update event. An
// artifact whose ArtifactID matches an existing one replaces it unless
// append is set, in which case its parts are appended.
Artifact(a Artifact, append bool) error
// Complete ends the task in TASK_STATE_COMPLETED with an optional
// final agent message.
Complete(parts ...Part) error
// Fail ends the task in TASK_STATE_FAILED.
Fail(parts ...Part) error
// Reject ends the task in TASK_STATE_REJECTED: the agent will not do
// this work (unknown skill, bad input).
Reject(parts ...Part) error
// RequireInput pauses the task in TASK_STATE_INPUT_REQUIRED. The next
// SendMessage addressed to the task resumes it: the handler runs again
// with the new message and the task's history.
RequireInput(parts ...Part) error
// RequireAuth pauses the task in TASK_STATE_AUTH_REQUIRED, the same
// way as RequireInput.
RequireAuth(parts ...Part) error
}
TaskContext is a handler's view of, and control over, its task. Every mutating call persists the task through the Store and fans the corresponding event out to streaming subscribers and push configs before returning, so a crash after the call cannot lose what the client was already told.
type TaskRecord ¶
type TaskRecord struct {
Task Task
Owner string
SkillID string
// Version is bumped on every UpdateTask; an update whose Version does
// not match the stored row fails with ErrConflict.
Version int64
CreatedAt time.Time
UpdatedAt time.Time
}
TaskRecord is a task as the Store holds it: the wire Task plus the bookkeeping the server needs and never sends.
func (*TaskRecord) Clone ¶
func (r *TaskRecord) Clone() *TaskRecord
Clone deep-copies the record: the Task via cloneTask, the scalars plainly.
type TaskState ¶
type TaskState string
TaskState is the lifecycle state of a task. Values serialize as the proto enum names (ProtoJSON), e.g. "TASK_STATE_COMPLETED".
const ( TaskStateUnspecified TaskState = "TASK_STATE_UNSPECIFIED" TaskStateSubmitted TaskState = "TASK_STATE_SUBMITTED" TaskStateWorking TaskState = "TASK_STATE_WORKING" TaskStateCompleted TaskState = "TASK_STATE_COMPLETED" TaskStateFailed TaskState = "TASK_STATE_FAILED" TaskStateCanceled TaskState = "TASK_STATE_CANCELED" TaskStateInputRequired TaskState = "TASK_STATE_INPUT_REQUIRED" TaskStateRejected TaskState = "TASK_STATE_REJECTED" TaskStateAuthRequired TaskState = "TASK_STATE_AUTH_REQUIRED" )
func (TaskState) Interrupted ¶
Interrupted reports whether the task is paused waiting on the client: input or authentication. A message addressed to an interrupted task resumes it; a stream closes when a task enters one of these states.
type TaskStatus ¶
type TaskStatus struct {
State TaskState `json:"state"`
Message *Message `json:"message,omitempty"`
Timestamp *Timestamp `json:"timestamp,omitempty"`
}
TaskStatus is the current state of a task plus the agent's most recent status message, if any.
type TaskStatusUpdateEvent ¶
type TaskStatusUpdateEvent struct {
TaskID string `json:"taskId"`
ContextID string `json:"contextId"`
Status TaskStatus `json:"status"`
Metadata map[string]any `json:"metadata,omitempty"`
}
TaskStatusUpdateEvent announces a state change on a streaming connection or a push notification.
type Timestamp ¶
Timestamp serializes as RFC 3339 in UTC with millisecond precision, the form the spec shows ("2025-10-28T14:25:33.142Z"). Decoding accepts any RFC 3339 string.
func Now ¶
func Now() *Timestamp
Now returns the current time as a Timestamp, truncated to milliseconds so a value survives a JSON round trip byte-for-byte.
func (Timestamp) MarshalJSON ¶
MarshalJSON implements json.Marshaler.
func (*Timestamp) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.