wire

package
v0.39.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package wire is the ONLY code in the platform that decodes opencode wire bytes (HTTP bodies and /event SSE envelopes). The API-side adapter, the SSE tracker's metering path, and the in-pod agentd's usage parser all consume it; nothing else may import it. Shapes are pinned by golden fixtures in ../testdata — see the opencode upgrade runbook.

Parsing is dual-shape tolerant by design. Two reasons with live-capture evidence (see testdata/REFRESH.md): (1) the same logical event type is named differently on different surfaces — the /event SSE stream emits unsuffixed types ("message.part.updated") while the persisted event store emits version-suffixed types ("message.part.updated.1"); (2) the fleet runs mixed opencode versions during image rollouts. Decoders accept every supported shape rather than branching on versions at runtime.

Index

Constants

View Source
const (
	EventTypeSessionStatus      = "session.status"
	EventTypeSessionCreated     = "session.created"
	EventTypeSessionUpdated     = "session.updated"
	EventTypeSessionIdle        = "session.idle"
	EventTypeMessagePartUpdated = "message.part.updated"
	EventTypeMessagePartDelta   = "message.part.delta"
)

knownEventTypes is the pinned event taxonomy: every type observed in the golden fixtures (live /event capture + persisted store; see ../testdata/REFRESH.md) plus legacy event-system names retained for mixed-fleet rollout tolerance. The fixture-coverage test forces every fixture type to be listed here, so a fixture refresh that introduces a type extends this set in the same change. Event-type literals (the dialect seam — Rule 12): consumers outside pkg/agent/opencode reference these constants, never raw strings (repolint: agent-event-literals gate).

View Source
const SessionErrorNameUserAbort = "MessageAbortedError"

SessionErrorNameUserAbort is the abort error name on the session.error event — wire-proven by the #1722 probe (real opencode 1.18.10 + 1.18.15): a user-initiated abort emits session.error {name: "MessageAbortedError"} BEFORE the idle burst. A mid-generation provider-stream cut emits NO session.error at all — the assistant message completes with finish "unknown" instead (probe 2026-10-18, #1732). That asymmetry is the abort-classification discriminator: the abort marker is an EVENT, infra loss is a FINISH.

View Source
const TurnFinishUnknown = "unknown"

TurnFinishUnknown is the assistant finish value meaning the provider stream ENDED WITHOUT a finish marker — the wire shape of an interrupted generation (mid-stream connection cut, #1732 probe): opencode completes the assistant message with {"finish": "unknown", "time": {"completed": ...}} and emits a clean idle transition, NO session.error. "stop" is the clean end; "length" is the context-window end; both are normal.

Variables

This section is empty.

Functions

func IsKnownEventType added in v0.16.1

func IsKnownEventType(eventType string) bool

IsKnownEventType reports whether eventType belongs to the pinned opencode taxonomy (version suffixes tolerated: the persisted store emits suffixed variants of the same logical types). Unknown types are not errors — they are the drift signal consumers count and warn on (#739 Gap 2: a taxonomy rename must be observable, not silent).

func IsPartDelta added in v0.16.1

func IsPartDelta(eventType string) bool

IsPartDelta reports whether eventType is a message-part delta event, tolerating the store surface's version suffix ("message.part.delta.1").

func IsPartUpdated

func IsPartUpdated(eventType string) bool

IsPartUpdated reports whether eventType is a message-part update. The live /event stream emits "message.part.updated"; the persisted event store emits "message.part.updated.N" — both must match (see REFRESH.md).

func IsSessionUpdated

func IsSessionUpdated(eventType string) bool

IsSessionUpdated reports whether eventType is a session.updated event, tolerating the store surface's version suffix ("session.updated.1").

func IsStepEnded

func IsStepEnded(eventType string) bool

IsStepEnded reports whether eventType is the legacy per-step usage event (pre-1.18.10). The legacy name is unversioned.

func ParseMessageLevelProgress added in v0.38.0

func ParseMessageLevelProgress(eventType string, props json.RawMessage) (string, bool)

ParseMessageLevelProgress classifies a message.updated event as MESSAGE-LEVEL progress per the #1685 owner ruling: progress means a message was COMPLETED by the LLM or SENT by the user. The completion anchor is info.time.completed — present on final message.updated upserts and absent on mid-flight ones in every golden fixture (all four testdata captures; pinned by TestGoldenFixture_MessageLevelProgress_CompletionAnchor). Ruling R1 (rework r3): the previous len(info.Cost)==0 discriminator was inert on the real wire — "cost" rides EVERY upsert as a JSON number (including the in-flight pre-streaming creation upsert, where it is 0), so it separated nothing and every assistant upsert stamped. Cost shape is deliberately not consulted here; note this is a deliberate divergence from the ABI translator's MESSAGE_END discriminator (translate_abi.go:192, msg.GetCost() != nil), which the projection's upsert stitching tolerates but the evidence clock must not: billing and hung-evidence answer different questions. Stream chunks (message.part.*), tool events, and heartbeats are NOT progress and must not reset the no-message clock. Suffix-tolerant like every event-type matcher here. Returns the owning session ID and whether the event counts; ("", false) for everything else.

func SessionIDFromProps added in v0.30.1

func SessionIDFromProps(props json.RawMessage) string

SessionIDFromProps extracts the owning session from one event's properties payload (#1342: the SSE progress signal — any event carrying a sessionID is evidence that session's turn is alive). Empty when the payload carries none (sessionless events).

Types

type AssistantTurnEnd added in v0.39.0

type AssistantTurnEnd struct {
	SessionID string
	Finish    string
}

AssistantTurnEnd is the terminal classification of one assistant turn: the final message.updated upsert carrying the completion anchor (per ParseMessageLevelProgress's anchor rule) plus the finish value.

func ParseAssistantTurnEnd added in v0.39.0

func ParseAssistantTurnEnd(eventType string, props json.RawMessage) (AssistantTurnEnd, bool)

ParseAssistantTurnEnd parses the TERMINAL upsert of an assistant turn (role "assistant", info.time.completed present). Returns ok=false for user messages, mid-flight upserts, and every other event shape.

type Envelope

type Envelope struct {
	ID         string          `json:"id"`
	Type       string          `json:"type"`
	Properties json.RawMessage `json:"properties"`
}

Envelope is the /event SSE wire envelope shared by all event types.

type SessionError added in v0.39.0

type SessionError struct {
	SessionID string
	Name      string
	Message   string
}

SessionError is the parsed session.error event payload.

func ParseSessionError added in v0.39.0

func ParseSessionError(eventType string, props json.RawMessage) (SessionError, bool)

ParseSessionError parses a session.error event into its session ID and error identity. Suffix-tolerant like every event-type matcher here. Returns ok=false for every other event shape.

type SessionUsage

type SessionUsage struct {
	SessionID    string
	ModelID      string
	ProviderID   string
	InputTokens  int64
	OutputTokens int64
	CostUSD      float64
	// CostMalformed reports a cost field that is neither a number nor
	// an object with a numeric "cost" — decoded as CostUSD=0 so billing
	// proceeds, but callers should warn: it is a wire-drift signal.
	CostMalformed bool
}

SessionUsage is the decoded session-level CUMULATIVE usage and model attribution from session.updated events — the metering/billing signal. Unlike StepUsage (per-step occupancy), these counters only grow; delta computation is the caller's policy, not the decoder's.

func ParseSessionUpdated

func ParseSessionUpdated(eventType string, raw string) (SessionUsage, bool, error)

ParseSessionUpdated decodes a session.updated event's cumulative usage and model attribution. ok=false (no error) when the event carries no info block — session.updated legitimately fires without one (early lifecycle). err is non-nil only for usage-bearing payloads that fail to decode (wire drift). Envelope-based, like ParseStepUsage.

func ParseSessionUpdatedProps

func ParseSessionUpdatedProps(props json.RawMessage) (SessionUsage, bool, error)

ParseSessionUpdatedProps is ParseSessionUpdated for callers holding already-stripped properties (the SSE tracker's dispatch path).

type StepUsage

type StepUsage struct {
	SessionID string
	Tokens    Tokens
}

StepUsage is the decoded per-step usage signal from any supported wire shape: the session it belongs to and that step's token accounting.

func ParseStepUsage

func ParseStepUsage(eventType string, raw string) (StepUsage, bool, error)

ParseStepUsage decodes a raw /event payload carrying per-step usage: the legacy standalone step-ended event (≤1.15.x era) or a part-update event whose part is a step-finish (1.18.x). ok=false when the event carries no usage (any other part type, or an event type that never carries usage). err is non-nil when the event TYPE claims usage but the payload fails to decode — that is wire drift and must surface, not silently drop.

The eventType check runs before any byte conversion or JSON decode, so the non-usage majority of stream traffic (deltas are the hottest and largest events) costs only string compares through this function. Matched part-update events pay one envelope decode — inherent, since only the payload says whether the part is a step-finish.

func ParseStepUsageProps

func ParseStepUsageProps(eventType string, props json.RawMessage) (StepUsage, bool, error)

ParseStepUsageProps is ParseStepUsage for callers that already stripped the envelope (agentd's legacy nested-payload path). Same semantics.

type Tokens

type Tokens struct {
	Total     int64 `json:"total,omitempty"`
	Input     int64 `json:"input"`
	Output    int64 `json:"output"`
	Reasoning int64 `json:"reasoning"`
	Cache     struct {
		Read  int64 `json:"read"`
		Write int64 `json:"write"`
	} `json:"cache"`
}

Tokens mirrors opencode's token-usage shape, stable across 1.15.x–1.18.x.

func (Tokens) PromptTokens

func (t Tokens) PromptTokens() int64

PromptTokens returns the raw prompt occupancy in opencode's accounting convention: input + cache.read + cache.write. Providers with no cache convention report it all in input; the sum stays correct.

Jump to

Keyboard shortcuts

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