Documentation
¶
Index ¶
- Constants
- Variables
- func ElicitationResultToSelectionPayload(raw json.RawMessage, opts []interrupt.UserChoice) (action string, resolution []byte, err error)
- func IsClientError(err error) bool
- func JSONRPCErrorCode(err error) int
- func ParseToolPermissionFromInterruptData(data []byte) (interruptID string, perm interrupt.ToolPermissionInterrupt, err error)
- func ParseUserSelectionFromInterruptData(data []byte) (interruptID string, opts []interrupt.UserChoice, err error)
- func PermissionToACPParams(sessionID, toolCallID string, perm interrupt.ToolPermissionInterrupt) map[string]any
- func PublicError(err error) error
- func RequestPermissionResultToPayload(raw json.RawMessage) (resolution []byte, cancelled bool, err error)
- func SelectionToElicitationParams(sessionID, toolCallID, question string, opts []interrupt.UserChoice) (map[string]any, error)
- type AgentSpec
- type ClientBridge
- type ClientCapabilities
- type ConfigOption
- type ConfigOptionValue
- type Conn
- type Connection
- type ConnectionRegistry
- type ElicitationResult
- type EventStream
- type HTTPRoute
- type InterruptEventEnvelope
- type MemoryWireStore
- type MessageWriter
- type PostgresWireStore
- type Protocol
- type ProtocolEnv
- type ProtocolWireStore
- type Registry
- func (r *Registry) AgentModel(agentID string) tacklr.InferenceStrategy
- func (r *Registry) CancelSession(sessionID string)
- func (r *Registry) ConfigOptions(currentAgent string) []ConfigOption
- func (r *Registry) DefaultAgent() string
- func (r *Registry) DropLiveHarness(sessionID string)
- func (r *Registry) HasAgent(agentID string) bool
- func (r *Registry) RecordSessionCreated(ctx context.Context)
- func (r *Registry) Register(agentID string, spec AgentSpec)
- func (r *Registry) RunTurn(ctx context.Context, req TurnRequest) (*EventStream, error)
- type RegistryOption
- type RequestPermissionResult
- type Server
- type SessionView
- type StreamControl
- type TurnRequest
Constants ¶
const ( HeaderAcpConnectionID = "Acp-Connection-Id" HeaderAcpSessionID = "Acp-Session-Id" // CookieAcpAffinity sticks a client to the same backend for one connection. CookieAcpAffinity = "acp_affinity" )
Header names for the ACP Streamable HTTP / WebSocket transport (RFD).
Variables ¶
var ( ErrInvalidRequest = errors.New("invalid request") ErrMethodNotFound = errors.New("method not found") ErrAgentNotFound = errors.New("agent not found") ErrSessionNotFound = errors.New("session not found") ErrSessionStoreNotConfigured = errors.New("session store is not configured") ErrStreamingNotSupported = errors.New("streaming not supported") ErrInternal = errors.New("internal server error") )
Sentinel errors for classification via errors.Is / errors.As.
var ErrRequestCancelled = errors.New("request cancelled")
ErrRequestCancelled is returned when a request is aborted via session/cancel or context cancellation.
var ErrWireSessionUnsupported = errors.New("wire sessions not supported by this protocol")
ErrWireSessionUnsupported is returned by Protocol session methods when the protocol has no wire-session lifecycle (e.g. simple SSE).
Functions ¶
func ElicitationResultToSelectionPayload ¶
func ElicitationResultToSelectionPayload(raw json.RawMessage, opts []interrupt.UserChoice) (action string, resolution []byte, err error)
ElicitationResultToSelectionPayload maps an accept response to the harness interrupt resolution payload. Returns action and optional selection JSON.
func IsClientError ¶
IsClientError reports whether err is safe to surface to the client (validation, not-found, bad payload). Internal failures return false.
func JSONRPCErrorCode ¶
JSONRPCErrorCode maps err to a JSON-RPC 2.0 error code.
func ParseToolPermissionFromInterruptData ¶
func ParseToolPermissionFromInterruptData(data []byte) (interruptID string, perm interrupt.ToolPermissionInterrupt, err error)
ParseToolPermissionFromInterruptData extracts a tool permission interrupt from yield data.
func ParseUserSelectionFromInterruptData ¶
func ParseUserSelectionFromInterruptData(data []byte) (interruptID string, opts []interrupt.UserChoice, err error)
ParseUserSelectionFromInterruptData extracts options from StreamEventInterrupt Data payload shape {"interruptId":"...","data":<serialized UserSelectionInterrupt>}.
func PermissionToACPParams ¶
func PermissionToACPParams(sessionID, toolCallID string, perm interrupt.ToolPermissionInterrupt) map[string]any
PermissionToACPParams builds session/request_permission params.
func PublicError ¶
PublicError returns a wire-safe error: client errors pass through unchanged; all other errors become ErrInternal so internal details are not leaked.
func RequestPermissionResultToPayload ¶
func RequestPermissionResultToPayload(raw json.RawMessage) (resolution []byte, cancelled bool, err error)
RequestPermissionResultToPayload maps a client permission response to the harness resolution payload. cancelled yields a non-nil err suitable for ending the turn.
func SelectionToElicitationParams ¶
func SelectionToElicitationParams(sessionID, toolCallID, question string, opts []interrupt.UserChoice) (map[string]any, error)
SelectionToElicitationParams builds form-mode elicitation/create params from a user-selection interrupt.
Types ¶
type AgentSpec ¶
type AgentSpec struct {
Name string
Config tacklr.Config
Model tacklr.InferenceStrategy
Tools []*tacklr.Tool
MCPConfigs []mcp.MCPConfig
SubAgents []*tacklr.SubAgent
WatchDog tacklr.AgentWatchDog
Store stores.BaseStore
// ExaAPIKey enables built-in web_search and web_fetch (or use process EXA_API_KEY).
ExaAPIKey string
}
type ClientBridge ¶
type ClientBridge struct {
// Caps is protected by mu; use GetCaps/SetCaps from concurrent stdio handlers.
Caps ClientCapabilities
// contains filtered or unexported fields
}
ClientBridge sends JSON-RPC requests to the Client and demuxes responses by id. Safe for concurrent Call from tool/turn goroutines; one bridge per connection.
func NewClientBridge ¶
func NewClientBridge(w MessageWriter) *ClientBridge
NewClientBridge creates a bridge that writes requests through w.
func (*ClientBridge) Call ¶
func (b *ClientBridge) Call(ctx context.Context, method string, params any) (json.RawMessage, error)
Call sends a JSON-RPC request and waits for the matching response or ctx cancel.
func (*ClientBridge) GetCaps ¶
func (b *ClientBridge) GetCaps() ClientCapabilities
GetCaps returns a snapshot of client capabilities (safe for concurrent use).
func (*ClientBridge) SetCaps ¶
func (b *ClientBridge) SetCaps(c ClientCapabilities)
SetCaps stores client capabilities (safe for concurrent use).
func (*ClientBridge) TryCompleteResponse ¶
func (b *ClientBridge) TryCompleteResponse(body []byte) bool
TryCompleteResponse returns true if body is a JSON-RPC response that completed a waiter.
type ClientCapabilities ¶
ClientCapabilities captures client features from initialize.
func ParseClientCapabilities ¶
func ParseClientCapabilities(params json.RawMessage) ClientCapabilities
ParseClientCapabilities extracts elicitation mode support from initialize params.
type ConfigOption ¶
type ConfigOption struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Category string `json:"category"`
Type string `json:"type"`
CurrentValue string `json:"currentValue"`
Options []ConfigOptionValue `json:"options"`
}
ConfigOption describes a selectable session configuration option returned by session/new and session/load.
type ConfigOptionValue ¶
type ConfigOptionValue struct {
Value string `json:"value"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
ConfigOptionValue is one choice within a select-type ConfigOption.
type Conn ¶
type Conn struct {
Writer MessageWriter
RPC *ClientBridge
Caps ClientCapabilities
}
Conn is one client connection (stdio session, or a logical HTTP request scope).
type Connection ¶
type Connection struct {
ID string
Bridge *ClientBridge
Writer MessageWriter
// contains filtered or unexported fields
}
Connection is one client transport connection (WebSocket or Streamable HTTP). Harness sessions live in Registry; this is ephemeral wire state only.
func (*Connection) Context ¶
func (c *Connection) Context() context.Context
Context is cancelled when the connection is removed or shut down.
type ConnectionRegistry ¶
type ConnectionRegistry struct {
// contains filtered or unexported fields
}
ConnectionRegistry tracks active ACP connections by Acp-Connection-Id.
func NewConnectionRegistry ¶
func NewConnectionRegistry() *ConnectionRegistry
NewConnectionRegistry returns an empty registry.
func (*ConnectionRegistry) Create ¶
func (r *ConnectionRegistry) Create(bridge *ClientBridge, writer MessageWriter) *Connection
Create registers a new connection with a generated id and cancellable context. bridge/writer may be filled in after Create (WebSocket accept path).
func (*ConnectionRegistry) Get ¶
func (r *ConnectionRegistry) Get(id string) *Connection
Get returns the connection for id, or nil.
func (*ConnectionRegistry) Remove ¶
func (r *ConnectionRegistry) Remove(id string)
Remove deletes the connection and cancels its context. Safe if missing or r is nil.
type ElicitationResult ¶
type ElicitationResult struct {
Action string `json:"action"`
Content map[string]any `json:"content"`
}
ElicitationResult is the Client response to elicitation/create.
type EventStream ¶
type EventStream struct {
Events <-chan streaming.StreamEvent
Harness *tacklr.AgentHarness
// contains filtered or unexported fields
}
EventStream is a running agent turn. Events is closed when the current harness run finishes (complete, error, interrupt park) or the turn context is cancelled and the registry forwarder exits.
Cancel cancels the turn context (session/cancel). Close releases harness resources after the turn has ended. Callers typically:
defer func() { stream.Cancel(); stream.Close() }()
Harness remains usable after an interrupt park so ResumeInterrupts can run before Close.
func (*EventStream) Cancel ¶
func (s *EventStream) Cancel()
Cancel cancels the turn context so producers stop. Safe to call multiple times. Does not release the harness; call Close after the event pump finishes.
func (*EventStream) Cancelled ¶
func (s *EventStream) Cancelled() bool
Cancelled reports whether the turn context has been cancelled.
func (*EventStream) Close ¶
func (s *EventStream) Close()
Close releases harness resources (idempotent). Call after Cancel or stream end.
func (*EventStream) ResumeInterrupts ¶
func (s *EventStream) ResumeInterrupts(ctx context.Context, responses map[string][]byte) (<-chan streaming.StreamEvent, error)
ResumeInterrupts resolves pending interrupts and returns a new event stream from the same harness (ACP mid-turn elicitation resume).
func (*EventStream) TurnContext ¶
func (s *EventStream) TurnContext() context.Context
TurnContext is the context for this turn (cancelled by session/cancel or parent).
type HTTPRoute ¶
type HTTPRoute struct {
Method string // e.g. "POST"
Pattern string // e.g. "/" or "/resume"
Handler func(env ProtocolEnv, w http.ResponseWriter, r *http.Request)
}
HTTPRoute is one HTTP endpoint owned by a protocol.
type InterruptEventEnvelope ¶
type InterruptEventEnvelope struct {
InterruptId string `json:"interruptId"`
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
InterruptEventEnvelope is the harness StreamEventInterrupt Data shape.
func ParseInterruptEnvelope ¶
func ParseInterruptEnvelope(data []byte) (InterruptEventEnvelope, error)
ParseInterruptEnvelope extracts interrupt id, type, and raw data from a yield event.
type MemoryWireStore ¶
type MemoryWireStore struct {
// contains filtered or unexported fields
}
MemoryWireStore is an in-process ProtocolWireStore.
func NewMemoryWireStore ¶
func NewMemoryWireStore() *MemoryWireStore
NewMemoryWireStore returns an empty in-memory wire store.
func (*MemoryWireStore) Delete ¶
func (s *MemoryWireStore) Delete(_ context.Context, sessionID string) error
type MessageWriter ¶
type MessageWriter interface {
WriteResult(id json.RawMessage, result any) error
// WriteError writes a failure. Implementations should use PublicError /
// JSONRPCErrorCode so internal details are not leaked on the wire.
WriteError(id json.RawMessage, err error) error
WriteFrame(data []byte) error
}
MessageWriter is the transport-agnostic sink for protocol responses and streamed frames. Transports adapt HTTP, stdio, and WebSocket to this interface.
type PostgresWireStore ¶
type PostgresWireStore struct {
// contains filtered or unexported fields
}
PostgresWireStore implements ProtocolWireStore against Postgres. Table: public.protocol_wire_session (see stores/testdata/session_schema.sql). Shares a *pgx.Conn with stores.PostgresStore when desired; schema is separate from harness session checkpoints.
func NewPostgresWireStore ¶
func NewPostgresWireStore(conn *pgx.Conn, protocolKey string) *PostgresWireStore
NewPostgresWireStore wraps an existing pgx connection. protocolKey labels rows (e.g. "acp"); empty defaults to "acp".
func (*PostgresWireStore) Delete ¶
func (s *PostgresWireStore) Delete(ctx context.Context, sessionID string) error
type Protocol ¶
type Protocol interface {
Name() string
// HandleInbound processes one inbound message on a connection-oriented
// transport (stdio NDJSON). Pure-HTTP protocols may return nil without work.
HandleInbound(ctx context.Context, env ProtocolEnv, body []byte) error
// HTTPRoutes returns routes to mount for ServeHTTP. Nil/empty is fine.
HTTPRoutes() []HTTPRoute
// OnStreamEvent encodes one harness StreamEvent for the client connection.
OnStreamEvent(ctx context.Context, env ProtocolEnv, threadID string, stream *EventStream, ev streaming.StreamEvent, reqID json.RawMessage) StreamControl
// OnStreamClosed is called when the event channel closes without Finished.
OnStreamClosed(ctx context.Context, env ProtocolEnv, threadID string, reqID json.RawMessage, cancelled bool) error
// CreateSession allocates a wire session. params is protocol-defined JSON.
// Stateless protocols return ErrWireSessionUnsupported.
CreateSession(ctx context.Context, env ProtocolEnv, params json.RawMessage) (sessionID string, result any, err error)
// LoadSession reattaches a wire session (memory or durable wire store).
LoadSession(ctx context.Context, env ProtocolEnv, sessionID string, params json.RawMessage) (result any, err error)
// BindTurn maps a wire session + turn body into a Registry TurnRequest.
BindTurn(ctx context.Context, env ProtocolEnv, sessionID string, turnParams json.RawMessage) (TurnRequest, error)
// CloseSession drops live wire binding and may cancel an in-flight turn.
CloseSession(ctx context.Context, env ProtocolEnv, sessionID string) error
}
Protocol is a complete wire façade over Registry. Transports only provide Conn I/O; protocols own methods, routes, stream policy, and wire-session lifecycle (create/load/bind/close).
Multi-protocol model (ACP, SSE, future A2A):
- Registry.RunTurn produces protocol-agnostic streaming.StreamEvent values.
- runTurnStream pumps those events through OnStreamEvent / OnStreamClosed.
- Each Protocol maps StreamEvent → its client wire and owns any wire session state.
Adding a protocol should not require harness streaming changes.
var ( ACP Protocol = NewACPProtocol(NewMemoryWireStore()) SSE Protocol = SSEProtocol() )
Built-in protocol aliases.
ACP is a process-scoped default for simple apps (NewServer(reg, server.ACP)). Prefer NewACPProtocol(wire) when you need durable/shared wire state or test isolation. Tests that share a *Registry should use protocolForRegistry (via serveACPRaw) or acpTestServer — not this package-level value for multi-step session flows.
func ACPProtocol ¶
func ACPProtocol() Protocol
ACPProtocol returns a new ACP protocol with an in-memory wire store. Each call is a fresh instance (own live map + wire store).
func NewACPProtocol ¶
func NewACPProtocol(wire ProtocolWireStore) Protocol
NewACPProtocol returns an ACP protocol with optional durable wire store. Nil wire uses an in-memory ProtocolWireStore.
func NewACPProtocolMemory ¶
func NewACPProtocolMemory() Protocol
NewACPProtocolMemory is shorthand for NewACPProtocol(NewMemoryWireStore()). Use when mounting ACP alongside other protocols:
server.NewServer(reg, server.NewACPProtocolMemory(), server.SSE)
func NewACPProtocolPostgres ¶
NewACPProtocolPostgres is shorthand for ACP with a Postgres wire store on conn. protocolKey is "acp". Requires table public.protocol_wire_session (see stores/testdata/session_schema.sql).
server.NewServer(reg, server.NewACPProtocolPostgres(conn))
type ProtocolEnv ¶
type ProtocolEnv struct {
Registry *Registry
Conn *Conn
// Connections is the server-wide connection registry (WebSocket / Streamable HTTP).
// Nil for pure stdio or tests that only use HandleInbound.
Connections *ConnectionRegistry
}
ProtocolEnv is the domain + connection context passed into protocol handlers.
type ProtocolWireStore ¶
type ProtocolWireStore interface {
Put(ctx context.Context, sessionID string, payload []byte) error
Get(ctx context.Context, sessionID string) ([]byte, error)
Delete(ctx context.Context, sessionID string) error
}
ProtocolWireStore persists protocol-owned session envelopes (not harness checkpoints). Payload is opaque JSON defined by each protocol. May share a database connection with BaseStore without sharing schema.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry serves agents over wire protocols (ACP, SSE, and others).
func NewRegistry ¶
func NewRegistry(store stores.BaseStore, defaultAgent string, opts ...RegistryOption) *Registry
NewRegistry builds a registry. opts may set telemetry providers.
func (*Registry) AgentModel ¶
func (r *Registry) AgentModel(agentID string) tacklr.InferenceStrategy
AgentModel returns the inference strategy for a registered agent, or nil.
func (*Registry) CancelSession ¶
CancelSession cancels the in-flight turn context for the session (if any). Session state is preserved. The turn context is the single cancel signal for harness work, the registry forwarder, and runTurnStream. This is abort-only (ACP session/cancel). It does not start a new turn.
func (*Registry) ConfigOptions ¶
func (r *Registry) ConfigOptions(currentAgent string) []ConfigOption
ConfigOptions returns selectable agent config options for wire session responses.
func (*Registry) DefaultAgent ¶
DefaultAgent returns the registry default agent id.
func (*Registry) DropLiveHarness ¶
DropLiveHarness removes a cached harness (e.g. session/close). Next prompt reloads from store.
func (*Registry) RecordSessionCreated ¶
RecordSessionCreated records a session-created metric (called by protocols).
func (*Registry) RunTurn ¶
func (r *Registry) RunTurn(ctx context.Context, req TurnRequest) (*EventStream, error)
RunTurn starts a prompt or resume turn and returns a stream of events. Setup errors (unknown session/agent, validation) are returned synchronously. Runtime errors are delivered as StreamEventError on the channel.
If a turn is already in flight for the session, it is cancelled and allowed to finalize before this turn starts (mid-turn steer via session/prompt).
type RegistryOption ¶
type RegistryOption func(*Registry)
RegistryOption configures NewRegistry.
func WithMeterProvider ¶
func WithMeterProvider(mp metric.MeterProvider) RegistryOption
WithMeterProvider sets the MeterProvider for turn and tool metrics. Nil uses the process global meter.
func WithTracer ¶
func WithTracer(t trace.Tracer) RegistryOption
WithTracer sets an explicit Tracer for turn telemetry. WithTracerProvider is the usual choice for consistent instrumentation names.
func WithTracerProvider ¶
func WithTracerProvider(tp trace.TracerProvider) RegistryOption
WithTracerProvider sets the TracerProvider for turn telemetry (tracer name telemetry.InstrumentationName). Nil uses the process global.
type RequestPermissionResult ¶
type RequestPermissionResult struct {
Outcome struct {
Outcome string `json:"outcome"`
OptionID string `json:"optionId,omitempty"`
} `json:"outcome"`
}
RequestPermissionResult is the Client response to session/request_permission.
type Server ¶
type Server struct {
Registry *Registry
Protocols []Protocol
// Client is set for the active stdio connection (outbound Agent→Client RPC).
// Prefer Conn.RPC inside protocol handlers; this field supports demux on stdio.
Client *ClientBridge
// Connections tracks ACP WebSocket (and future Streamable HTTP) connections.
Connections *ConnectionRegistry
}
Server serves a Registry over one or more wire Protocols.
func NewACPServer ¶
NewACPServer returns a Server with ACP and an in-memory wire session store. Suitable for demos, single-process apps, and tests that do not need durable session/load across process restarts.
reg := server.NewRegistry(stores.NewInMemoryStore(), "agent")
reg.Register("agent", server.AgentSpec{...})
srv := server.NewACPServer(reg)
_ = srv.ServeStdio(ctx, os.Stdin, os.Stdout)
// or: _ = srv.ServeHTTP(ctx, ":8080") // /acp WebSocket + Streamable HTTP
func NewACPServerPostgres ¶
NewACPServerPostgres returns a Server with ACP backed by a Postgres wire store. The registry should already use a harness store (e.g. stores.NewPostgresStore(conn) or InMemoryStore). Wire and harness schemas are separate; sharing *pgx.Conn is fine.
harness := stores.NewPostgresStore(conn) reg := server.NewRegistry(harness, "agent") // reg.Register(...) srv := server.NewACPServerPostgres(reg, conn)
func NewACPServerWithWire ¶
func NewACPServerWithWire(reg *Registry, wire ProtocolWireStore) *Server
NewACPServerWithWire returns a Server with ACP using the given wire store for protocol session envelopes (session/new, session/load). The registry's BaseStore remains the harness checkpoint store (agent conversation state).
wire := server.NewPostgresWireStore(conn, "acp") srv := server.NewACPServerWithWire(reg, wire)
Or any custom ProtocolWireStore (Redis, SQLite, …).
func NewServer ¶
NewServer wraps a Registry and one or more protocols. The first protocol is used for connection-oriented transports (stdio).
func (*Server) HandleMessage ¶
func (s *Server) HandleMessage(ctx context.Context, body []byte, w MessageWriter)
HandleMessage dispatches one inbound body on the primary protocol. Used by tests and unary HTTP adapters.
type SessionView ¶
type SessionView struct {
SessionID string
ConfigOptions []ConfigOption
}
SessionView is the domain view of a wire session (returned by protocols).
type StreamControl ¶
type StreamControl struct {
Frames [][]byte
ReplaceEvents <-chan streaming.StreamEvent
Finished bool
Err error
}
StreamControl is the protocol's decision after observing one harness event.
type TurnRequest ¶
type TurnRequest struct {
SessionID string
AgentID string
ThreadID string
Prompt string
// UserMessage is multimodal user content (ACP). When set, preferred over Prompt.
UserMessage *tacklr.Message
Responses map[string]json.RawMessage
Load bool
// AllowMissingCheckpoint: when Load is true and the harness store has no
// row, start a fresh agent instead of failing. Set by wire BindTurn for
// sessions that may never have been checkpointed yet.
AllowMissingCheckpoint bool
// CWD is optional turn context (protocol may set from wire session).
CWD string
// MCPServers are session-scoped MCP configs for this turn.
MCPServers []mcp.MCPConfig
}
TurnRequest describes a prompt or resume turn.
Session mode (ACP): protocol BindTurn fills SessionID, AgentID, MCPServers, Load, and AllowMissingCheckpoint. Registry does not own wire envelopes.
Direct mode (SSE): set AgentID and ThreadID. Load restores from harness store.