workerws

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package workerws implements the official WebSocket adapter for the public HumanAgent WorkerTransport port. The wire protocol is intentionally small: domain identities and settlement receipts remain defined by package agent.

Index

Constants

View Source
const (
	// JournalContractID names the durable remote-worker inbox/outbox contract.
	// Its major version includes FIFO enumeration, exact-input replay, durable
	// settlement tombstones, and atomic event-to-rejection settlement.
	JournalContractID    framework.ContractID = "human.agent.worker.journal"
	JournalContractMajor uint16               = 1

	MaxJournalPageSize = 1024
)
View Source
const (
	// ProtocolVersion is bumped for incompatible wire changes. It is unrelated
	// to agent.WorkerTransportContractMajor, which versions the Go port.
	ProtocolVersion = "1"
	// SessionHeader is generated by a remote worker for each WebSocket dial. It
	// is never accepted as a durable task, command, or delivery identity.
	SessionHeader = "X-Human-Agent-Worker-Session"
)

Variables

View Source
var (
	ErrClientConfiguration  = errors.New("invalid Agent remote worker configuration")
	ErrClientClosed         = errors.New("Agent remote worker client is closed")
	ErrClientAuthentication = errors.New("Agent remote worker authentication was rejected")
	ErrClientProtocol       = errors.New("Agent remote worker protocol failed")
)
View Source
var (
	ErrJournalDescription   = errors.New("invalid Agent worker journal description")
	ErrJournalClosed        = errors.New("Agent worker journal is closed")
	ErrJournalNotFound      = errors.New("Agent worker journal record was not found")
	ErrJournalConflict      = errors.New("Agent worker journal binding or delivery conflicts with durable state")
	ErrJournalCorrupt       = errors.New("Agent worker journal contains a corrupt record")
	ErrJournalCommitUnknown = errors.New("Agent worker journal commit outcome is unknown")
	ErrJournalLimit         = errors.New("Agent worker journal read limit was exceeded")
)
View Source
var (
	ErrNotStarted           = errors.New("Agent worker WebSocket transport is not started")
	ErrAlreadyStarted       = errors.New("Agent worker WebSocket transport is already started")
	ErrAuthentication       = errors.New("Agent worker WebSocket authentication failed")
	ErrInvalidConfiguration = errors.New("invalid Agent worker WebSocket configuration")
)

Functions

func JournalRequirements

func JournalRequirements() framework.Requirements

Types

type AuthenticateFunc

type AuthenticateFunc func(context.Context, *http.Request) (Identity, error)

func (AuthenticateFunc) AuthenticateWorker

func (function AuthenticateFunc) AuthenticateWorker(
	ctx context.Context,
	request *http.Request,
) (Identity, error)

type Authenticator

type Authenticator interface {
	AuthenticateWorker(context.Context, *http.Request) (Identity, error)
}

Authenticator belongs to this HTTP adapter, not the Agent domain. A host can inspect mTLS, cookies, bearer tokens, or its own session and return canonical authority/worker identity. Implementations must not trust identity fields in a WebSocket message body. The implementation is borrowed until the transport runtime reaches Done, called concurrently, and must honor context cancellation. The request is borrowed for the call and must not be retained or mutated. Return a framework Fault with CodeUnauthenticated/CodeForbidden and RetryNever only for a proved terminal credential decision. Temporary provider failures use CodeUnavailable/RetryBackoff; unclassified errors fail closed as HTTP 503 so a durable worker keeps reconnecting instead of treating the token as revoked.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a durable remote Agent worker runtime. Assignments and Rejections are replayed from Journal after process restart until explicitly confirmed.

func NewClient

func NewClient(ctx context.Context, config ClientConfig) (*Client, error)

NewClient validates composition and starts reconnect and replay loops. ctx bounds construction only; Shutdown owns the successful runtime lifetime.

func (*Client) Assignments

func (client *Client) Assignments() <-chan agent.WorkerAssignmentDelivery

func (*Client) ConfirmAssignment

func (client *Client) ConfirmAssignment(ctx context.Context, delivery agent.WorkerDeliveryID) error

ConfirmAssignment removes an assignment from the application inbox and retains a compact digest tombstone so a lost wire ACK cannot re-present it.

func (*Client) ConfirmRejection

func (client *Client) ConfirmRejection(ctx context.Context, delivery agent.WorkerDeliveryID) error

ConfirmRejection removes one NACK from the application inbox. Its compact event settlement tombstone remains for exact replay/conflict detection.

func (*Client) Done

func (client *Client) Done() <-chan struct{}

func (*Client) Err

func (client *Client) Err() error

func (*Client) Rejections

func (client *Client) Rejections() <-chan RejectedEvent

func (*Client) SendEvent

func (client *Client) SendEvent(ctx context.Context, delivery agent.WorkerEventDelivery) error

SendEvent commits an exact event to the durable outbox before making it eligible for transmission. Exact settled replay is a no-op; ID reuse with a different payload returns ErrJournalConflict.

func (*Client) Shutdown

func (client *Client) Shutdown(ctx context.Context) error

type ClientConfig

type ClientConfig struct {
	URL       string
	Gateway   GatewayID
	Authority agent.AuthorityID
	Worker    agent.WorkerID
	Journal   framework.Resource[Journal]

	HTTPHeader     http.Header
	HeaderProvider HeaderProvider
	HTTPClient     *http.Client

	ConnectTimeout      time.Duration
	WriteTimeout        time.Duration
	ReadLimit           int64
	ReconnectMinDelay   time.Duration
	ReconnectMaxDelay   time.Duration
	ReconnectResetAfter time.Duration
	ReleaseTimeout      time.Duration
	NotificationBuffer  int
}

ClientConfig configures the official remote Agent worker. Journal is the only durability boundary and must implement JournalContractMajor exactly. URL must use ws or wss and must not contain userinfo or a fragment.

type Config

type Config struct {
	GatewayID      GatewayID
	Authenticator  Authenticator
	ReadLimit      int64
	WriteTimeout   time.Duration
	PingInterval   time.Duration
	PingTimeout    time.Duration
	OriginPatterns []string
}

Config controls one official WebSocket adapter. The embedding application owns the HTTP server/listener and mounts Transport as a handler.

type GatewayID

type GatewayID string

GatewayID is the durable identity of one gateway consistency domain. It is not a URL: changing DNS, ports, or transports does not change GatewayID.

func (GatewayID) Validate

func (identity GatewayID) Validate() error

type HeaderProvider

type HeaderProvider interface {
	WorkerHeaders(context.Context) (http.Header, error)
}

HeaderProvider supplies per-dial authentication headers. It may refresh a short-lived credential on every reconnect. Returned maps are cloned before use and SessionHeader is always overwritten by Client. The provider is borrowed until Client.Done closes, is called by one connection loop, and must honor context cancellation without retaining the returned Header.

type HeaderProviderFunc

type HeaderProviderFunc func(context.Context) (http.Header, error)

func (HeaderProviderFunc) WorkerHeaders

func (function HeaderProviderFunc) WorkerHeaders(ctx context.Context) (http.Header, error)

type Identity

type Identity struct {
	Authority agent.AuthorityID
	Worker    agent.WorkerID
}

Identity is the durable worker identity returned by Authenticator. Session identity comes from the transport handshake and is deliberately absent.

type Journal

type Journal interface {
	Description() JournalDescription
	// Bind is an idempotent durability operation. The first call permanently
	// records binding; an exact call succeeds and any different value returns
	// ErrJournalConflict. All other operations fail closed while unbound.
	Bind(context.Context, JournalBinding) error

	PutAssignment(context.Context, JournalAssignment) (JournalEntryState, error)
	ConfirmAssignment(context.Context, agent.WorkerDeliveryID) error
	ListAssignments(context.Context, JournalSequence, int) ([]JournalAssignment, error)

	PutEvent(context.Context, JournalEvent) (JournalEntryState, error)
	ListEvents(context.Context, JournalSequence, int) ([]JournalEvent, error)
	SettleEvent(context.Context, agent.WorkerEventReceipt, JournalDigest, JournalDigest) error

	ListRejections(context.Context, JournalSequence, int) ([]JournalRejection, error)
	ConfirmRejection(context.Context, agent.WorkerDeliveryID) error
}

Journal is the persistence port borrowed or owned by Client through a framework.Resource. It deliberately has no Close method.

Implementations take ownership of inputs before a successful return and return independent copies from List methods; mutable byte slices must never alias adapter storage or another caller. All operations other than Description and Bind fail closed until a durable binding exists.

Every mutating call is one atomic, strictly serializable transaction. Calls are not implicitly retried by the implementation. A successful call is durable before it returns. ErrJournalCommitUnknown means the caller must reconcile by repeating the exact operation; it must never assume success or failure.

PutAssignment and PutEvent allocate a non-zero monotonically increasing sequence for a new ID. An exact existing digest returns its current state and does not allocate a sequence. A different digest for any pending or settled ID returns ErrJournalConflict. ConfirmAssignment replaces the pending payload with a compact settled tombstone atomically.

SettleEvent validates the pending event identity, removes it from the outbox, and writes a compact settled tombstone atomically. For a NACK it atomically moves the full event and receipt into the rejection inbox; ConfirmRejection later compacts both payloads. Exact repeated receipt settlement is a no-op; a different event or receipt digest conflicts.

List methods return records with Sequence > after in strictly increasing order, up to limit. Callers may page without holding a transaction open.

type JournalAssignment

type JournalAssignment struct {
	Sequence JournalSequence
	Digest   JournalDigest
	Delivery agent.WorkerAssignmentDelivery
}

type JournalBinding

type JournalBinding struct {
	Gateway   GatewayID
	Authority agent.AuthorityID
	Worker    agent.WorkerID
}

JournalBinding prevents a durable outbox from being replayed into another gateway, authority, or worker after a configuration or URL change.

func (JournalBinding) Validate

func (binding JournalBinding) Validate() error

type JournalDescription

type JournalDescription struct {
	Contract framework.Contract
	Provider string
	Version  string
}

JournalDescription is static, non-secret adapter metadata. Provider and Version identify the implementation, not a database or tenant.

func (JournalDescription) Validate

func (description JournalDescription) Validate() error

type JournalDigest

type JournalDigest string

JournalDigest is a lowercase SHA-256 digest calculated by the official client over the canonical JSON representation of a public delivery/receipt. Journal implementations preserve it verbatim; they do not reinterpret the payload or choose their own equivalence relation.

func (JournalDigest) Validate

func (digest JournalDigest) Validate() error

type JournalEntryState

type JournalEntryState string

JournalEntryState tells PutAssignment/PutEvent whether the exact delivery is still pending or has a durable settlement tombstone. A settled exact replay is never presented or sent again. Reusing its ID with a different digest is a conflict even after the payload has been compacted.

const (
	JournalEntryPending JournalEntryState = "pending"
	JournalEntrySettled JournalEntryState = "settled"
)

type JournalError

type JournalError struct {
	Operation string
	Delivery  agent.WorkerDeliveryID
	Cause     error
}

JournalError attaches an operation and delivery identity while preserving errors.Is/errors.As classification through Cause.

func (*JournalError) Error

func (failure *JournalError) Error() string

func (*JournalError) Unwrap

func (failure *JournalError) Unwrap() error

type JournalEvent

type JournalEvent struct {
	Sequence JournalSequence
	Digest   JournalDigest
	Delivery agent.WorkerEventDelivery
}

type JournalRejection

type JournalRejection struct {
	Sequence      JournalSequence
	EventDigest   JournalDigest
	ReceiptDigest JournalDigest
	RejectedEvent
}

JournalRejection is the durable application-facing NACK inbox. Confirming it removes both payloads but retains their compact digest tombstone.

type JournalSequence

type JournalSequence uint64

JournalSequence is allocated monotonically by a Journal. It establishes FIFO presentation within each pending collection and is never reused.

type RejectedEvent

type RejectedEvent struct {
	Delivery agent.WorkerEventDelivery
	Receipt  agent.WorkerEventReceipt
}

RejectedEvent is the application-facing NACK, including the exact reply or command that was rejected so it can be corrected after a process restart.

type Transport

type Transport struct {
	// contains filtered or unexported fields
}

Transport is both a WorkerTransport provider and the handler mounted by the host. Start binds exactly one running endpoint; shutdown is performed on the returned runtime. A fresh Transport value is required for a later run.

func New

func New(config Config) (*Transport, error)

func (*Transport) Description

func (*Transport) ServeHTTP

func (transport *Transport) ServeHTTP(response http.ResponseWriter, request *http.Request)

func (*Transport) Start

func (transport *Transport) Start(
	ctx context.Context,
	endpoint agent.WorkerEndpoint,
) (agent.WorkerTransportRuntime, error)

Directories

Path Synopsis
Package sqlite provides the official durable SQLite implementation of the Agent remote-worker Journal port.
Package sqlite provides the official durable SQLite implementation of the Agent remote-worker Journal port.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL