sessionstate

package
v0.34.13 Latest Latest
Warning

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

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

Documentation

Overview

Package sessionstate is the pod-local session state authority (design 0055, Epic 69 US-69.2): a module-sealed subsystem that owns seq assignment (I1), the store-reseeded projection (I3/I4), the stamped snapshot + ordered event fanout (I2), the durable seq cursor on the platform/ PVC subPath (I9 prep), and the authenticated ABI surface (I8). It is dialect-free machinery: the harness event parser and store reader are injected seams (US-69.3 grows the opencode implementations).

Sealing rules (enforced by TestModuleSealDependencies): no imports of agentd supervision or any cmd/ package; callers reach the authority only through this package's exported API.

Index

Constants

View Source
const DeclaredTimeoutReason = "declared timeout exceeded"

DeclaredTimeoutReason is the synthetic terminal-state error folded onto tool parts terminated by #1576 layer 2 — past their own DECLARED timeout (the input's "timeout" milliseconds; the model specified the bound, not a heuristic).

View Source
const DefaultPlatformDir = "/platform"

DefaultPlatformDir is the platform/ PVC subPath mount (pod_builder). Overridable for tests via Config.PlatformDir.

View Source
const LeaseConvergenceBound = 30 * time.Second

LeaseConvergenceBound is the epic-71 lease clock: the shared convergence bound for every lease this authority holds (#1311's ledger deadlines and #1310's pending-input lease — ONE constant, proposed 30s per the stress matrix, tune before freezing).

View Source
const OrphanSweepReason = "harness restart"

OrphanSweepReason is the synthetic terminal-state error folded onto in-flight tool parts orphaned by a harness restart (#1342 S12 backstop): the part never received terminal state because the process died mid-turn; the restored projection reports it aborted so the UI renders honestly and the agent's next turn sees the tool died. The value equals the contract constant session.ToolAbortReasonHarnessRestart (the adapter's transcript repair closes already-orphaned parts with the same reason) — the equality is pinned by TestOrphanSweepReasonMatches- ContractConstant so the two repair sites never fork. The constant is duplicated rather than imported: the module seal (seal_test.go) admits only the ABI schema into this package.

View Source
const ReconcileCadence = LeaseConvergenceBound / 2

ReconcileCadence is the background reconcile-pass interval: half the convergence bound, so one missed tick still converges inside it.

Variables

View Source
var ErrNoStore = errors.New("sessionstate: no store reader wired")

ErrNoStore is returned by Reseed when no store reader is wired: the projection cannot be rebuilt from truth, so reseed fails loudly instead of silently trusting the (possibly stale) in-memory state.

Functions

This section is empty.

Types

type Actor

type Actor interface {
	Act(ctx context.Context, sessionID string, action *abiv1.ActionRequest) (*abiv1.ActionResult, error)
}

Actor is the dialect seam for action execution: perform one typed action against the harness (localhost :4096 in production; the wiring layer injects the opencode implementation — same seam class as Admitter). The authority has already validated the union member and verified capability declaration. Transcript verbs (interrupt, switch, compact) execute UNDER the session's single-flight lock, so a slow action delays that session's admissions (the sole-writer contract); answer verbs execute lock-free under their own forward budget.

type Admitter

type Admitter interface {
	Admit(ctx context.Context, sessionID, messageID, text, model string) (string, error)
}

Admitter is the dialect seam for admission: POST the harness's prompt endpoint. The wiring layer injects the opencode implementation (localhost :4096, Basic §D1 credential — same seam class as EventParser/StoreReader). messageID is the entry-derived dedupe key ("msg_" + entryID, S2/#1315): the pinned harness validates it (msg-prefix), uses it verbatim as the user message's store ID, and UPSERTS on collision — the same-ID re-POST returns the existing exchange instead of appending (G1-verified on opencode 1.18.15). Returns the harness's response message ID for promotion correlation.

type Authority

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

Authority is the module-sealed session state authority. All mutable state lives under mu: seq assignment, the projection, and fanout registration share one lock so the stamp is atomic by construction.

func New

func New(cfg Config) (*Authority, error)

New constructs the authority and loads the durable seq cursor from PlatformDir. It does NOT reseed — callers run Reseed(ReseedBoot) once the store reader is reachable (boot ordering is the caller's decision).

func (*Authority) CheckStalls

func (a *Authority) CheckStalls(ctx context.Context) StallStats

CheckStalls runs one stall-detection pass: admitted rows older than the promotion deadline move to stalled (fsync'd), and the configured Wake fires once per newly-stalled row. No-op without a ledger.

func (*Authority) Close

func (a *Authority) Close() error

Close persists the final cursor and releases resources. Idempotent (once-guarded): test cleanups and shutdown paths may both close.

func (*Authority) Deliver

func (*Authority) Events

func (*Authority) Handler

func (a *Authority) Handler() (string, http.Handler)

Handler returns the ABI surface: the generated connect service behind Basic auth (I8 — every route authenticated, zero exceptions). The returned mount path is the connect procedure root; agentd's user mux serves it as-is.

func (*Authority) InFlightDeliveries added in v0.27.0

func (a *Authority) InFlightDeliveries() int64

InFlightDeliveries is the ledger's unresolved delivery count (ledgered + admitted + stalled) — design 0055 M4's flip-gate drain signal. Promoted/turn-ended/failed rows are resolved from the flip's perspective (failed is terminal per attempt; the outbox owns retries).

func (*Authority) Ingest

func (a *Authority) Ingest(raw []byte)

Ingest applies one raw harness event. It is the recover wall: parser panics and decode failures are contained (counted + logged), never propagated — agentd must not die from projection input. During a reseed the payload is buffered instead of applied (I3 ordering).

func (*Authority) IngestForTest

func (a *Authority) IngestForTest(evt *abiv1.Event)

IngestForTest applies a contract event directly, bypassing the parser seam (fault-injection + comparator support: the projection fold is exercised without dialect fixtures).

func (*Authority) KillForTest

func (a *Authority) KillForTest()

KillForTest simulates SIGKILL semantics for the kill-9 fault-injection test: resources are abandoned WITHOUT graceful cleanup (no cursor close, no subscriber teardown) exactly as a hard process death would.

func (*Authority) Metrics

func (a *Authority) Metrics() Metrics

func (*Authority) ParserFailuresForTest added in v0.27.2

func (a *Authority) ParserFailuresForTest() int64

ParserFailuresForTest exposes the parser-failure counter (the #1291 r5 shape-drift accounting pin).

func (*Authority) PlatformDir

func (a *Authority) PlatformDir() string

PlatformDir reports the durable-cursor directory (suspend/resume diagnostics and the S1 scenario harness's restart scenarios).

func (*Authority) Reconcile added in v0.29.0

func (a *Authority) Reconcile(ctx context.Context) ReconcileStats

Reconcile runs one store-evidence convergence pass over the ledger and the BUSY projection. Serialized against Reseed (reseedMu); per-session row advancement runs under the session's single-flight lock (TryLock — skip, never wait) so it can never interleave with an in-flight admission nor block behind one. Store I/O happens outside every authority lock and is bounded by the pass deadline (M3.1: no synchronous harness call on a hot path — this is a background/ cadence pass, never request-scoped).

func (*Authority) ReplayUnresolvedDeliveries

func (a *Authority) ReplayUnresolvedDeliveries(ctx context.Context)

ReplayUnresolvedDeliveries drives admission for ledgered rows after a crash/suspend resume (M2 pod-death path: exactly-once per attempt — admitted rows are skipped by construction, I6).

func (*Authority) Reseed

func (a *Authority) Reseed(ctx context.Context, reason ReseedReason) error

Reseed runs the ordered, race-free reseed procedure (design 0055 M1): quiesce → buffer inbound → reseed from the store → emit projection.reseeded (consuming a seq) → flush buffered → resume live. Serialized by reseedMu; the store read happens OUTSIDE mu so ingest and stream reads stay hot (M3.1).

func (*Authority) SetAdmissionDeadlineForTest added in v0.29.0

func (a *Authority) SetAdmissionDeadlineForTest(d time.Duration)

SetAdmissionDeadlineForTest reshapes the LEDGERED admission deadline (#1311's fault-injection harness: a negative value makes rows instantly past-deadline without sleeping the real 1m window).

func (*Authority) SetLeaseBoundForTest added in v0.29.0

func (a *Authority) SetLeaseBoundForTest(d time.Duration)

SetLeaseBoundForTest overrides the busy-lease expiry bound (≤0 forces immediate expiry; 0 restores the default).

func (*Authority) SetParserForTest added in v0.27.2

func (a *Authority) SetParserForTest(p EventParser)

SetParserForTest swaps the parser seam (the #1291 r6 restart-mid-turn e2e: a fresh translator instance models the process restarting with an empty tool-call memo).

func (*Authority) SetReconcileTimeoutForTest added in v0.29.0

func (a *Authority) SetReconcileTimeoutForTest(d time.Duration)

SetReconcileTimeoutForTest reshapes the pass's evidence-I/O deadline (fault-injection harnesses).

func (*Authority) SetStallDeadlineForTest

func (a *Authority) SetStallDeadlineForTest(d time.Duration)

SetStallDeadlineForTest shrinks the promotion-stall deadline (the fault-injection harness makes a row cross it without waiting 10m).

func (*Authority) SetStoreForTest

func (a *Authority) SetStoreForTest(s StoreReader)

SetStoreForTest swaps the store reader (fault-injection support).

func (*Authority) State

func (a *Authority) State() StateSnapshot

State returns the atomically-stamped projection snapshot.

func (*Authority) Stream

func (a *Authority) Stream(ctx context.Context) (<-chan *abiv1.StreamFrame, func(), error)

Stream subscribes to the ordered frame stream. The mandatory connection ordering (I2): (1) register the per-connection buffer, (2) capture the stamped snapshot under the projection lock, (3) flush buffered frames with seq > stamp, (4) go live. Canceling the returned func tears the subscription down.

type Config

type Config struct {
	PlatformDir string
	Parser      EventParser
	Store       StoreReader
	Passwords   []string
	RateLimit   RateLimitConfig
	Logger      *zap.Logger
	// ABIVersion is reported in the capability report snapshot frame.
	ABIVersion string
	// Capabilities, when set, is the capability report served on the
	// snapshot frame (provenance, supported actions, supported part
	// kinds). Built by the wiring layer from boot-time facts (0053 pins,
	// versions, D3 part limits) — the report is STATIC per boot so the
	// hot path never touches the harness (M3.1).
	Capabilities *abiv1.CapabilityReport
	// Admitter (US-69.7) is the delivery-admission seam; nil DISABLES
	// the ledger (Deliver returns NotSupported) — the authority owns
	// nothing dialect-specific.
	Admitter Admitter
	// AdmitterTimeout bounds one synchronous V1 admission (#1313: the
	// response IS the LLM turn, 30-120s). Zero ⇒ the 3-minute default.
	// Injectable so the #1315 incident repro can drive the hung-turn
	// window without wall-clock waits.
	AdmitterTimeout time.Duration
	// AnswerTimeout (#1396) bounds one ANSWER_QUESTION forward. The
	// forward targets the harness ask registry and completes in
	// milliseconds (53-307ms measured); zero ⇒ answerForwardBudget (5s).
	// Injectable so the budget leg tests expire it without wall-clock
	// waits (the AdmitterTimeout precedent).
	AnswerTimeout time.Duration
	// Actor (US-69.9) is the typed-actions seam; nil DISABLES the Act op
	// (NotSupported) — the wiring layer injects the opencode executor.
	Actor Actor
	// Wake (US-69.12) is the stall-recovery seam: agentd's best-effort
	// nudge when an admitted row crosses the promotion deadline
	// (checkStalls fires it once per row). Nil = no-op wake (stalls
	// still surface via metrics/alerts; wake failures count only when a
	// wake exists).
	Wake func(ctx context.Context, sessionID string) error
	// FastCursor disables per-event fsync (scenario harnesses replaying
	// event BURSTS; durability is covered by the fault-injection suite).
	FastCursor bool
}

Config wires the authority. PlatformDir holds the durable seq cursor (platform/ PVC subPath in production; any dir in tests).

type EventParser

type EventParser interface {
	Parse(raw []byte) (evt *abiv1.Event, ok bool, err error)
}

EventParser translates one raw harness SSE payload into a contract event. ok=false means the payload carries nothing projectable (dropped, counted); err signals a decode failure of a payload that CLAIMED to be projectable (wire drift — logged, never fatal). Implementations must be safe for concurrent use.

type LedgerState

type LedgerState int

LedgerState is the M2 state machine:

ledgered → admitted → promoted → turn-ended

with `failed` (per attempt, terminal for THAT attempt — re-armable at attempt+1) and `stalled` (admitted past the promotion deadline — recovered by WAKE ONLY, never re-admitted; I6).

const (
	LedgerStateUnspecified LedgerState = iota
	LedgerStateLedgered
	LedgerStateAdmitted
	LedgerStatePromoted
	LedgerStateTurnEnded
	LedgerStateStalled
	LedgerStateFailed
)

func (LedgerState) String

func (s LedgerState) String() string

String returns the lowercase wire name (metric labels, logs).

type Metrics

type Metrics struct {
	DroppedEvents     int64
	ParserFailures    int64
	PanicsContained   int64
	CustomValveEvents int64
	Subscribers       int
	BufferedPending   int
	// SecondsSinceSeqAdvance is how long since the projection last moved
	// (the R5 starvation signal: seq stalled N seconds while the pod
	// runs). Initialized at construction; a reseed counts as advance.
	SecondsSinceSeqAdvance float64
	// LedgerDepths is the per-state row count (the ledger funnel).
	// Nil when no ledger is wired.
	LedgerDepths map[string]int64
	// StalledEntries is the current stalled-row count (the #1119 class,
	// visible).
	// LeaseResolved/LeaseAppeared are the cumulative pending-lease diff
	// outcomes (#1310 slice B: asks resolved by absence; asks appeared
	// from live truth); LeaseGatherFails counts cadence pending-gather
	// failures (the unnoticed-source-failure signal).
	LeaseResolved    int64
	LeaseAppeared    int64
	LeaseGatherFails int64
	// OrphanPartsAborted is the cumulative count of in-flight tool parts
	// folded as aborted by the #1342 harness-restart sweep (S12's
	// backstop — every count is a turn that died without terminal state).
	OrphanPartsAborted int64
	StalledEntries     int64
	// OldestPromotionStallSeconds is the age of the oldest
	// admitted-unpromoted row (0 when none).
	OldestPromotionStallSeconds float64
	// Reconcile counts are the cumulative #1311 sweep outcomes (S7's
	// observable surface) plus evidence-read failures (rows left
	// untouched, retried next pass).
	ReconcilePromoted      int64
	ReconcileTurnEnded     int64
	ReconcileFailed        int64
	ReconcileBusyCleared   int64
	ReconcileEvidenceFails int64
	// AnswerBudgetExceeded is the cumulative count of answer forwards
	// that exceeded AnswerTimeout (#1396): the typed retryable
	// DeadlineExceeded path — every count is a would-be 125s hang made
	// visible and fast.
	AnswerBudgetExceeded int64
}

Metrics exposes module counters for ops_metrics wiring (US-69.12).

type RateLimitConfig

type RateLimitConfig struct {
	Burst        int
	RefillPerSec float64
}

RateLimitConfig bounds per-session mutating-op rates (I8).

type ReconcileStats added in v0.29.0

type ReconcileStats struct {
	// Promoted / TurnEnded / Failed count rows the pass advanced.
	Promoted  int
	TurnEnded int
	Failed    int
	// DeclaredTimeouts counts tool parts terminated by #1576 layer 2 —
	// past their own declared deadline (store-independent, runs first).
	DeclaredTimeouts int
	// BusyCleared counts sessions whose BUSY view was re-derived idle
	// (evidence idle + no unresolved rows remain).
	BusyCleared int
	// EvidenceFailures counts store-evidence reads that errored — rows
	// were left untouched (never an authoritative empty) and retry on the
	// next pass.
	EvidenceFailures int
	// LeaseResolved/LeaseAppeared: #1310 slice B pending-lease diff
	// outcomes (asks resolved by absence; asks appeared from live truth).
	LeaseResolved int
	LeaseAppeared int
}

ReconcileStats reports one reconcile pass's outcome (S7's observable surface: what the sweep advanced, what it could not).

type ReseedReason

type ReseedReason int

ReseedReason classifies why a projection reseed ran.

const (
	ReseedReasonBoot ReseedReason = iota
	ReseedReasonGenerationChange
	// ReseedReasonStallWake (US-69.12): the stall watchdog reseeded
	// because an admitted row crossed the promotion deadline — the
	// store refresh is the wake (events completing the turn promote or
	// turn-end the row; a still-missing promotion escalates via the
	// stalled-entries/wake-failure alerts).
	ReseedReasonStallWake
)

type SessionSeed

type SessionSeed struct {
	Status        abiv1.SessionStatus
	PendingInputs []*abiv1.InputRequest
}

SessionSeed is the store-truth seed for one session at reseed time (I3/I4): the authoritative status plus the pending inputs the store still holds. Busy/streaming and in-flight parts are NEVER seeded — they are live-turn state, rebuilt by events after the reseed.

type SessionView

type SessionView struct {
	Status abiv1.SessionStatus
	// Busy is the DERIVED #1574 busy truth (streaming || in-flight
	// parts || queue depth) — not the raw streaming flag. Components
	// carry the why.
	Busy           bool
	BusyComponents *abiv1.BusyComponents
	InFlightParts  []*abiv1.Part
	PendingInputs  []*abiv1.InputRequest
}

SessionView is one session's projected state (I12: a snapshot of these fields alone renders the session).

type StallStats

type StallStats struct {
	// Stalled is the number of rows moved admitted→stalled THIS pass.
	Stalled int
	// WakeFailures is the number of wake attempts that errored THIS pass
	// (the escalation signal: wake-only recovery failing).
	WakeFailures int
}

StallStats is one stall-watchdog pass's outcome (US-69.12).

type StateSnapshot

type StateSnapshot struct {
	Seq      uint64
	Sessions map[string]*SessionView
}

StateSnapshot is an atomically-stamped view of the projection (I1: the state is exactly the fold of events 1..Seq).

type StoreReader

type StoreReader interface {
	SessionStates(ctx context.Context) (map[string]SessionSeed, error)
	MessagePresence(ctx context.Context, sessionID string, messageIDs []string) (map[string]bool, error)
	// PendingInputs is the lease diff's truth source (#1310 slice B):
	// the harness live ask registries, keyed by session. STRICT failure
	// semantics — unlike SessionStates (whose reseed tolerates
	// endpoint-absence-as-empty), any fetch that is not a clean read
	// returns an error and the caller MUST treat the pending set as
	// indeterminate (skip the diff), never as empty.
	PendingInputs(ctx context.Context) (map[string][]*abiv1.InputRequest, error)
}

StoreReader snapshots the harness store's session truth — the reseed source (I3/I4) and the reconcile pass's evidence (#1311). Implementations must respect ctx deadlines; a hung store must surface as ctx error, never block forever. MessagePresence answers which of the given message IDs the store currently holds; an error means NO evidence (the caller must treat the session's rows as unverifiable this pass — never as absent).

Jump to

Keyboard shortcuts

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