websocket

package
v0.1.0-alpha.2 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package streamhub provides task-scoped stream buffers for worker-push and client subscribe (SSE).

Package wsconn defines the typed event protocol for WebSocket communication between the portal frontend and the server. All messages are JSON envelopes with a "type" field and a typed "payload".

Index

Constants

View Source
const (
	TypeConversationCreate  = "conversation.create"
	TypeConversationMessage = "conversation.message"
	TypeSubscribeTask       = "subscribe.task"
	TypeUnsubscribeTask     = "unsubscribe.task"
)
View Source
const (
	TypeConversationCreated = "conversation.created"
	TypeMessageDelta        = "conversation.message.delta"
	TypeMessageQueued       = "conversation.message.queued"
	TypeMessageDequeued     = "conversation.message.dequeued"
	TypeMessageCompleted    = "conversation.message.completed"
	TypeConversationError   = "conversation.error"
	TypeTaskStatusChanged   = "task.status.changed"
	TypeTaskStreamDelta     = "task.stream.delta"
	TypeTaskStreamDone      = "task.stream.done"
	TypeSystemError         = "system.error"
)
View Source
const ErrorCodeQueueFull = "queue_full"

ErrorCodeQueueFull marks a conversation error that refused a new message because the conversation's queue is full. It is not a failure of the turn in progress, which is still running — a client must not read it as "the conversation went idle".

View Source
const StreamEventDone = "[[DONE]]"

StreamEventDone is sent on the subscription channel when the run finishes (SUCCEEDED/FAILED).

Variables

This section is empty.

Functions

func DecodePayload

func DecodePayload[T any](env Envelope) (T, error)

DecodePayload unmarshals the envelope's raw payload into the target type T.

func Encode

func Encode(eventType string, payload any) ([]byte, error)

Encode marshals an event type and payload into JSON bytes suitable for WebSocket write.

func Serve

func Serve(w http.ResponseWriter, r *http.Request, userID, teamID string, deps ConnDeps)

Serve upgrades an authenticated request and runs the connection until it closes. The caller has already decided who this is and which team they are in; this package does not repeat that.

Types

type Conn

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

Conn manages a single WebSocket connection for one authenticated user.

func (*Conn) RunSystemConversationTurn

func (wc *Conn) RunSystemConversationTurn(ctx context.Context, conversationID, message string)

RunSystemConversationTurn runs a system-triggered conversation turn (e.g. task completion). It queues behind whatever the user is doing in that conversation instead of blocking the caller's goroutine until the conversation is free.

type ConnDeps

type ConnDeps struct {
	Conversations model.ConversationStore
	Turns         *turnqueue.Registry
	// Turner runs one conversation turn. An interface so this package does not
	// depend on the service that assembles agents, models, and tools.
	Turner   Turner
	Registry *ConnRegistry
	// CORSOrigin is checked on the upgrade. Empty or "*" accepts any origin,
	// which is what a deployment serving Portal from the same host has.
	CORSOrigin string
}

ConnDeps is everything a live connection needs. It is a struct rather than a Handler because this package should not be able to reach a store it has no use for: a socket creates and reads conversations and runs turns, and that is the whole list.

type ConnRegistry

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

ConnRegistry tracks active WebSocket connections per user.

func NewConnRegistry

func NewConnRegistry() *ConnRegistry

func (*ConnRegistry) ForUser

func (r *ConnRegistry) ForUser(userID string) []*Conn

func (*ConnRegistry) OnTaskRunTerminal

func (r *ConnRegistry) OnTaskRunTerminal(ctx context.Context, info model.TaskRunTerminalInfo)

OnTaskRunTerminal is called when a task run reaches terminal status. It finds the user's active WebSocket connection and triggers a system Tier 1 conversation turn with the task result.

func (*ConnRegistry) Register

func (r *ConnRegistry) Register(userID string, c *Conn)

func (*ConnRegistry) Unregister

func (r *ConnRegistry) Unregister(userID string, c *Conn)

type ConversationCreate

type ConversationCreate struct {
	Channel string `json:"channel,omitempty"`
	Message string `json:"message"`
}

ConversationCreate is the payload for TypeConversationCreate.

type ConversationCreated

type ConversationCreated struct {
	ConversationID string `json:"conversation_id"`
}

ConversationCreated is the payload for TypeConversationCreated.

type ConversationError

type ConversationError struct {
	ConversationID string `json:"conversation_id,omitempty"`
	Error          string `json:"error"`
	// Code is an optional machine-readable reason. Empty means the turn itself failed.
	Code string `json:"code,omitempty"`
}

ConversationError is the payload for TypeConversationError.

type ConversationMessage

type ConversationMessage struct {
	ConversationID string `json:"conversation_id"`
	Content        string `json:"content"`
}

ConversationMessage is the payload for TypeConversationMessage.

type Envelope

type Envelope struct {
	Type    string          `json:"type"`
	Payload json.RawMessage `json:"payload"`
}

Envelope is the wire format for every WebSocket message (both directions).

func Decode

func Decode(data []byte) (Envelope, error)

Decode unmarshals raw bytes into an Envelope.

type MessageCompleted

type MessageCompleted struct {
	ConversationID string `json:"conversation_id"`
	// QueuedRemaining is how many messages are still waiting for their turn. A
	// client uses it to decide whether the conversation is idle or merely between
	// turns.
	QueuedRemaining int `json:"queued_remaining,omitempty"`
}

MessageCompleted is the payload for TypeMessageCompleted.

type MessageDelta

type MessageDelta struct {
	ConversationID string `json:"conversation_id"`
	Delta          string `json:"delta"`
}

MessageDelta is the payload for TypeMessageDelta.

type MessageDequeued

type MessageDequeued struct {
	ConversationID string `json:"conversation_id"`
	Content        string `json:"content"`
}

MessageDequeued is the payload for TypeMessageDequeued: a queued message is starting its own turn now.

type MessageQueued

type MessageQueued struct {
	ConversationID string `json:"conversation_id"`
	Content        string `json:"content"`
	// Position is 1-based: 1 is the next turn to run after the current one.
	Position int `json:"position"`
}

MessageQueued is the payload for TypeMessageQueued: the message arrived while a turn was running and will run as its own turn once that one finishes.

type StreamHub

type StreamHub interface {
	// Append adds a delta to the buffer and broadcasts to all subscribers for that task.
	Append(taskID, delta string)
	// Buffer returns the current buffered content for the task. Empty string if task has no buffer or was Done.
	Buffer(taskID string) string
	// Done marks the task's current run as finished; sends StreamEventDone to subscribers and clears state.
	Done(taskID string)
	// Subscribe returns a channel of deltas (and finally StreamEventDone) for the task. unsub must be called when done.
	Subscribe(taskID string) (events <-chan string, unsub func())
}

StreamHub is the interface for task-scoped stream buffers. Keys are task_id (one active run per task). Implementations may be in-memory (single instance) or backed by Redis (multi-instance).

func NewStreamHub

func NewStreamHub() StreamHub

NewStreamHub returns an in-memory StreamHub. Multi-instance scaling requires a Redis-backed impl.

type SubscribeTask

type SubscribeTask struct {
	TaskID string `json:"task_id"`
}

SubscribeTask is the payload for TypeSubscribeTask.

type SystemError

type SystemError struct {
	Error string `json:"error"`
}

SystemError is the payload for TypeSystemError.

type TaskStatusChanged

type TaskStatusChanged struct {
	TaskID string `json:"task_id"`
	Status string `json:"status"`
	Title  string `json:"title,omitempty"`
}

TaskStatusChanged is the payload for TypeTaskStatusChanged.

type TaskStreamDelta

type TaskStreamDelta struct {
	TaskID string `json:"task_id"`
	Delta  string `json:"delta"`
}

TaskStreamDelta is the payload for TypeTaskStreamDelta.

type TaskStreamDone

type TaskStreamDone struct {
	TaskID string `json:"task_id"`
}

TaskStreamDone is the payload for TypeTaskStreamDone.

type Turner

type Turner interface {
	HandleTurn(ctx context.Context, cmd conversation.HandleTurnCmd) (conversation.ConversationResult, error)
}

Turner runs one conversation turn to completion, streaming as it goes.

type UnsubscribeTask

type UnsubscribeTask struct {
	TaskID string `json:"task_id"`
}

UnsubscribeTask is the payload for TypeUnsubscribeTask.

Jump to

Keyboard shortcuts

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