protocolstate

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package protocolstate owns the preferred-strategy missing-signal protocol state: a bounded progress fingerprint, bounded counters, and an independent opaque state token.

It is a pure value package. It owns no runtime, terminal, provider, transcript, verifier, or admission authority, and it holds no globals, goroutines, durable maps, or stored contexts. Callers receive validated numeric limits from the parent feature; this package deliberately does not import the parent agentloopguard package, which would create a cycle.

Counting semantics: Reprompts counts emitted, validated continuation intents. Observations never consume a reprompt, and new progress never replenishes the immutable total budget. Platform continuation attempts are a different unit; a positive platform snapshot cap constrains candidate attempts and is intersected with, never substituted for, the feature budget.

Index

Constants

View Source
const (
	MinReprompts       = 1
	MaxReprompts       = 3
	MinNoProgressLimit = 1
	MaxNoProgressLimit = 64

	// MaxConsecutiveNoProgress is the absolute supported bounded no-progress
	// count. Observation saturates here instead of emitting an invalid value.
	MaxConsecutiveNoProgress = MaxNoProgressLimit

	// FingerprintPrefix and FingerprintHexDigits fix the canonical digest
	// representation: "sha256:" plus 64 lowercase hex characters.
	FingerprintPrefix    = "sha256:"
	FingerprintHexDigits = 64
)

V1 bounds. MaxReprompts is the absolute accepted total cap for a V1 token; the effective cap is additionally narrowed by the configured limit and by platform continuation policy.

View Source
const (
	TokenPrefix = "alg-proto-v1."
	// MaxTokenBytes keeps the whole token within the SDK opaque-identifier
	// bound of 256 bytes.
	MaxTokenBytes = 256
)

TokenPrefix is the independent preferred-strategy state namespace. It is deliberately distinct from the legacy `alg-state-v1` progress token and is never translated to or from it.

Variables

View Source
var (
	ErrInvalidState       = errors.New("agent-loop-guard protocol state: invalid state")
	ErrInvalidLimits      = errors.New("agent-loop-guard protocol state: invalid limits")
	ErrInvalidPlatform    = errors.New("agent-loop-guard protocol state: invalid platform snapshot")
	ErrInvalidToken       = errors.New("agent-loop-guard protocol state: invalid token")
	ErrRepromptsExhausted = errors.New("agent-loop-guard protocol state: reprompt budget exhausted")
)

Bounded classification errors. Wrapped errors stay errors.Is-compatible and never carry raw state tokens or evidence text.

Functions

func Encode

func Encode(state State) (string, error)

Encode serializes only bounded counters and the canonical fingerprint into an independent opaque protocol token. Only valid State values are encodable; an invalid value never produces a token and never consumes a reprompt slot.

func Fingerprint

func Fingerprint(in terminaldecision.Input) string

Fingerprint hashes the stable canonical evidence concepts: normalized objective, candidate, and recent text; canonical cause; the explicit-completion expectation and observation booleans; and the active ordered canonical action kind/status/name tuples.

Volatile identity and budget metadata are excluded: candidate and action identifiers, request and leg identifiers, lineage references, timestamps and deadlines, policy revisions, continuation attempts, configured limits, platform caps, and verifier verdicts. Only the active fixed-array action count is iterated, so unused capacity is never hashed.

Fingerprint is a pure projection helper. It performs no platform validation: an overflowing ActionCount is clamped rather than panicking, and it does not weaken terminaldecision.ValidateInput, which still rejects evidence that is populated beyond ActionCount.

Types

type Eligibility

type Eligibility struct {
	// Allowed reports whether another reprompt may be considered.
	Allowed bool
	// Remaining is the intersection of configured and platform slots and is
	// never negative.
	Remaining int
}

Eligibility is the pure result of intersecting the configured total cap, the no-progress limit, the terminal flag, and a positive platform cap.

func Remaining

func Remaining(state State, limits Limits, platform Platform) (Eligibility, error)

Remaining intersects the configured total budget with platform continuation capacity, using the correct units for each.

The configured cap counts Reprompts. The platform snapshot cap counts candidate Continuation.Attempt values, so it is compared against the current attempt, never against Reprompts. Only a positive snapshot cap constrains this feature; a zero cap imposes no feature-side default because core owns its own resolution. New progress never replenishes the configured total.

type Limits

type Limits struct {
	MaxReprompts    int
	NoProgressLimit int
}

Limits carries validated numeric bounds supplied by the parent feature. This package does not define configuration defaults: passing the accepted task-6.1 normalized values is the caller's responsibility.

func (Limits) Validate

func (l Limits) Validate() error

Validate checks the supported finite ranges 1..MaxReprompts and 1..MaxNoProgressLimit.

type Platform

type Platform struct {
	Cap     int
	Attempt int
}

Platform is the candidate-attempt view of the platform continuation cap. Cap counts Continuation.Attempt values, not protocol reprompts. A non-positive Cap is an unresolved platform default that this package must not copy or reinterpret; core owns that default.

func (Platform) Validate

func (p Platform) Validate() error

Validate rejects negative snapshots. Cap zero and negative-cap semantics beyond that remain platform-owned.

type State

type State struct {
	// Reprompts counts emitted, validated continuation intents.
	Reprompts int
	// LastFingerprint is "" or the canonical SHA-256 digest representation.
	LastFingerprint string
	// ConsecutiveNoProgress counts equal consecutive observations.
	ConsecutiveNoProgress int
	// Terminal is the absorbing stop flag.
	Terminal bool
}

State is a request-scoped value holding only bounded counters and a stable progress digest, so a caller can own it without globals or shared state.

An empty LastFingerprint means no evidence baseline has been established yet. Because observation precedes intent emission, an empty fingerprint cannot carry nonzero reprompt or no-progress counters. A nonempty fingerprint with zero Reprompts is valid: establishing a baseline is not an emitted intent. Terminal is absorbing and preserves the other three fields.

func Advance

func Advance(prior State, limits Limits) (State, error)

Advance returns the proposed next state for one successfully validated continuation intent, incrementing Reprompts exactly once.

The prior state is returned unchanged on every error path, so a caller can build and validate its intent against the proposed state and only then commit it. Encode or build failures therefore never consume a slot, and platform admission is neither retried nor counted as another emitted intent. Progress never resets this immutable total.

An emitted intent implies an observed baseline, so an unobserved prior state cannot yield a valid proposal. The proposed state is checked against the same central invariants before success is reported, so this helper never returns a state that its own Validate or Encode would reject. Counters are never reset and no fingerprint is invented to make a proposal valid.

func Decode

func Decode(token string) (State, error)

Decode validates and decodes one opaque protocol token.

Decoding is structural corruption detection, not authentication: it rejects unknown versions and flags, malformed or noncanonical base64url, oversize or truncated payloads, trailing payload, out-of-range counters, malformed fingerprints, and internally inconsistent combinations.

Validation uses the absolute V1 bound of MaxReprompts and is independent of any lower currently configured cap, so a token carrying Reprompts two stays valid under a configured cap of one; stopping in that case is eligibility's job, not a decode failure. Malformed tokens are never reset to zero state.

func Observe

func Observe(prior State, fingerprint string) (State, error)

Observe records one evidence observation and returns the next state.

The first stable evidence establishes the baseline with a consecutive no-progress count of zero. An equal fingerprint increments the bounded consecutive count; a changed fingerprint resets only that count.

Observation never increments Reprompts, because Reprompts counts emitted intents rather than observations or platform admissions. A terminal state is absorbing and is returned unchanged with all four fields preserved.

Invalid prior state is rejected rather than reset or clamped, and the consecutive count saturates at its absolute bound instead of overflowing.

func (State) Validate

func (s State) Validate() error

Validate checks state validity without repairing it. An invalid Reprompts value is never reset or clamped here or by Observe/Remaining.

Jump to

Keyboard shortcuts

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