contextstate

package
v0.1.3 Latest Latest
Warning

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

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

Documentation

Overview

Package contextstate is the CLI's durable context contract layer. It composes the upstream mivia-ai-sdk/contextstate transport structs with the CLI's identity, redaction, worktree, and lifecycle extensions.

The byte-identical subset (shape constants and the ref-mint primitives) is re-exported from mivia-ai-sdk/contextstate so every CLI caller sees the SDK's canonical implementation under one symbol. The divergent subset (ContentRef with the CLI's "ctxp_<hex>" primary-key namespace, CheckpointID with the CLI's SummaryModel field, the commit/checkpoint records with the CLI's Principal, fingerprint, and worktree-instance fields, and the 13 CLI sentinel errors) stays as distinct local types so the CLI's failure modes and persisted shape do not move with an SDK release.

Index

Constants

View Source
const (
	// MaxIdentifierBytes is the byte cap on every transport identifier.
	MaxIdentifierBytes = sdkctx.MaxIdentifierBytes
	// MaxPayloadReferenceBytes is the byte cap on a payload reference.
	MaxPayloadReferenceBytes = sdkctx.MaxPayloadReferenceBytes
	// MaxSourceRangeEvents is the inclusive span that keeps range arithmetic honest.
	MaxSourceRangeEvents = sdkctx.MaxSourceRangeEvents
	// HashPrefix is the SDK's canonical content-address prefix.
	HashPrefix = sdkctx.HashPrefix

	// Namespace is the CLI's local payload namespace. The SDK exposes
	// none, and the CLI's ContentRef rejects every other value at
	// Validate time, so this stays local.
	Namespace = "mivia.context.payload.v1"

	// DefaultMaxCheckpointMetadata bounds a checkpoint's summary_metadata column.
	DefaultMaxCheckpointMetadata = 16 * 1024
	// DefaultMaxSummaryMetadata bounds the persisted summary envelope.
	DefaultMaxSummaryMetadata = 12 * 1024
	// MaxAuditBytes bounds an audit record's serialized size.
	MaxAuditBytes = 1 * 1024
)

Shape bounds the SDK pins FOR the transport structs, re-exported so callers read "contextstate.MaxIdentifierBytes" rather than reaching into the SDK directly. Volume bounds live in the local Limits type (limits.go); they are operator-owned and uncapped by default.

View Source
const DefaultPayloadChunkBytes = 64 << 10 // 64 KiB

DefaultPayloadChunkBytes is the built-in source-event payload chunk size when SourceEventBytes is 0 (uncapped volume). Large payloads are split into ordered chunks of this size under one content ref; they are never rejected for whole-payload size under defaults.

View Source
const MaxSessionDirBytes = 4096

MaxSessionDirBytes bounds the stored session directory string so a hostile or corrupt row cannot inflate every picker payload without limit.

View Source
const MaxSessionTitleBytes = 256

MaxSessionTitleBytes bounds a title that the terminal can render safely.

Variables

View Source
var (
	// ErrInvalidDTO wraps every CLI DTO validation failure.
	ErrInvalidDTO = errors.New("invalid context DTO")
	// ErrPrincipalMismatch marks a payload whose owner tuple does not match the writing principal.
	ErrPrincipalMismatch = errors.New("principal mismatch")
	// ErrSessionNotFound marks a read of an unknown session.
	ErrSessionNotFound = errors.New("session not found")
	// ErrSessionTombstoned marks a read of a session that has been deleted.
	ErrSessionTombstoned = errors.New("session tombstoned")
	// ErrPayloadNotFound marks a read of an unknown payload.
	ErrPayloadNotFound = errors.New("payload not found")
	// ErrPayloadRevoked marks a Get of a payload marked revoked.
	ErrPayloadRevoked = errors.New("payload revoked")
	// ErrExportTooLarge marks a context export that broke the ExportBytes bound.
	ErrExportTooLarge = errors.New("context export too large")
	// ErrSummaryUnavailable marks a read of a summary that has not yet been built.
	ErrSummaryUnavailable = errors.New("summary unavailable")
	// ErrStaleRevision marks a commit against a moved revision.
	ErrStaleRevision = errors.New("stale revision")
	// ErrStaleBinding marks a commit against a moved binding.
	ErrStaleBinding = errors.New("stale binding")
	// ErrCheckpointConflict marks a reused operation key carrying a different request.
	ErrCheckpointConflict = errors.New("checkpoint conflict")
	// ErrWorktreeDeleted marks a commit or read against a deleted worktree instance.
	ErrWorktreeDeleted = errors.New("worktree session deleted")
	// ErrPromptBudgetExceeded marks a prompt that broke the model's input budget.
	ErrPromptBudgetExceeded = errors.New("prompt budget exceeded")
	// ErrSessionLiveElsewhere marks a ReclaimSession attempt against a session
	// whose current owner's lease is still fresh (an actively-heartbeating
	// process), rather than the session being unknown, tombstoned, or owned by
	// a different subject/managed worktree.
	ErrSessionLiveElsewhere = errors.New("context session is live in another process")
)

CLI sentinels. The names intentionally overlap with the SDK's sentinels (ErrSessionNotFound, ErrStaleRevision, ...); callers write errors.Is(err, contextstate.ErrSessionNotFound) and resolve to the CLI's, not the SDK's, so storage code that wraps %w produces a caller-visible chain that points back at this package.

Functions

func Digest

func Digest(chunks ...[]byte) string

Digest returns the SDK's SHA-256 of the ordered concatenation of chunks, as 64 lowercase hex characters. The CLI never mixes namespace or owner fields into the digest.

func EffectiveCheckpointMetadataLimit

func EffectiveCheckpointMetadataLimit() int

EffectiveCheckpointMetadataLimit returns the operator-configured checkpoint metadata bound, falling back to the compiled-in default.

func EffectiveSummaryMetadataLimit

func EffectiveSummaryMetadataLimit() int

EffectiveSummaryMetadataLimit returns the operator-configured summary metadata bound, falling back to the compiled-in default when uncapped.

func Exceeds

func Exceeds(size, bound int) bool

Exceeds is exceedsLimit for hosts outside this package, so every layer asks the same question about a bound and a zero never reads as "allow nothing".

func FingerprintCommitRequest

func FingerprintCommitRequest(r CommitRequest) (string, error)

FingerprintCommitRequest hashes the canonical request fields while omitting the fingerprint itself. Storage uses it to distinguish safe retries from a reused operation key carrying different state.

func IsRef

func IsRef(s string) bool

IsRef reports whether ref matches the SDK's canonical "sha256:<64 hex>" shape. The CLI's own contentRefID mints "ctxp_<hex>" strings that fail this check, which is the point: a CLI reference is not an SDK reference.

func MarshalCanonical

func MarshalCanonical(value any) ([]byte, error)

MarshalCanonical encodes a DTO with sorted object keys and no insignificant whitespace. Validation remains the responsibility of the DTO's Validate method; this function only canonicalizes the JSON representation.

func Mint

func Mint(chunks ...[]byte) string

Mint returns the SDK's canonical content address of the concatenated chunks: HashPrefix plus Digest. The CLI's own contentRefID is a different function with a different prefix.

func NewContentRef

func NewContentRef(namespace, workspaceID, sessionID, subjectID string, chunks ...[]byte) (sdkctx.ContentRef, error)

NewContentRef mints an SDK-shaped ContentRef. The CLI's own contentRefID minter lives in sanitize.go.

func NormalizeSessionTitle

func NormalizeSessionTitle(title string) (string, error)

NormalizeSessionTitle validates and trims user-facing session title text.

func ParseCatalogTimestamp

func ParseCatalogTimestamp(s string) time.Time

ParseCatalogTimestamp parses a SessionCatalogInfo timestamp written by either producer (see sqliteDefaultTimestampLayout). It returns the zero time when s does not match either layout, mirroring time.Parse's error contract for callers that already treat a parse failure as "unknown".

func PayloadChunkSize

func PayloadChunkSize() int

PayloadChunkSize returns the effective per-chunk byte size for source-event payloads. Zero / negative SourceEventBytes settles to DefaultPayloadChunkBytes so storage always has a finite chunk granularity without whole-payload reject.

func SetLimits

func SetLimits(limits Limits)

SetLimits installs the operator's durable ceilings process-wide. The host calls it once during startup from configuration, exactly as it installs the redaction policy; a process that never calls it stays uncapped, so any path that runs before startup - tests, `mivia version`, a store constructed directly - is uncapped rather than falling back to a compiled ceiling.

func UnmarshalCanonical

func UnmarshalCanonical(data []byte, target any) error

func ValidSessionDir

func ValidSessionDir(dir string) bool

ValidSessionDir reports whether dir is safe to persist: no NUL bytes and within the length bound. The empty string is valid (no directory recorded).

func ValidateSourceEvent

func ValidateSourceEvent(event SourceEvent) error

ValidateSourceEvent is the named source-boundary validator used by storage and import adapters. The implementation delegates to the DTO validator so callers have one stable entry point for source records.

func ValidateSourceEvents

func ValidateSourceEvents(events []SourceEvent, sessionID string, firstSequence uint64) error

Types

type AdvanceRequest

type AdvanceRequest struct {
	OperationID        string           `json:"operation_id"`
	Principal          Principal        `json:"principal"`
	SessionID          string           `json:"session_id"`
	Expected           Revision         `json:"expected"`
	ExpectedBinding    BindingRevision  `json:"expected_binding"`
	NewSession         uint64           `json:"new_session"`
	NewDurable         uint64           `json:"new_durable"`
	NewSourceSequence  uint64           `json:"new_source_sequence"`
	NewBinding         BindingRevision  `json:"new_binding"`
	ActiveCheckpointID string           `json:"active_checkpoint_id,omitempty"`
	ClearActive        bool             `json:"clear_active"`
	Reason             string           `json:"reason"`
	WorktreeInstance   WorktreeInstance `json:"worktree_instance,omitempty"`
}

func (AdvanceRequest) Validate

func (r AdvanceRequest) Validate() error

type AuditAction

type AuditAction string
const (
	AuditDelete AuditAction = "delete"
	AuditExport AuditAction = "export"
	AuditImport AuditAction = "import"
)

type AuditRecord

type AuditRecord struct {
	ID          string         `json:"id"`
	Action      AuditAction    `json:"action"`
	WorkspaceID string         `json:"workspace_id"`
	SessionID   string         `json:"session_id"`
	SubjectID   string         `json:"subject_id"`
	Revision    uint64         `json:"revision"`
	Size        int            `json:"size"`
	Retention   RetentionClass `json:"retention"`
	CreatedAt   time.Time      `json:"created_at"`
	ExpiresAt   time.Time      `json:"expires_at"`
}

type BindingRevision

type BindingRevision struct {
	Provider   string `json:"provider"`
	Model      string `json:"model"`
	Generation uint64 `json:"generation"`
}

BindingRevision names the provider-model pair and its generation.

func NewBindingRevision

func NewBindingRevision(providerName, model string, generation uint64) (BindingRevision, error)

NewBindingRevision builds one BindingRevision and validates it.

func (BindingRevision) Validate

func (b BindingRevision) Validate() error

Validate bounds both identifiers and requires a positive generation.

type CheckpointID

type CheckpointID struct {
	SessionID      string      `json:"session_id"`
	SourceRange    SourceRange `json:"source_range"`
	Algorithm      string      `json:"algorithm"`
	SchemaVersion  uint32      `json:"schema_version"`
	SummaryModel   string      `json:"summary_model"`
	IdempotencyKey string      `json:"idempotency_key"`
}

CheckpointID identifies one checkpoint within a session. The CLI adds SummaryModel so storage can record which model authored the summary; the SDK has no such field, so this type stays local.

func NewCheckpointID

func NewCheckpointID(sessionID string, sourceRange SourceRange, algorithm string, schemaVersion uint32, summaryModel, key string) (CheckpointID, error)

NewCheckpointID builds one CheckpointID and validates it.

func (CheckpointID) Validate

func (id CheckpointID) Validate() error

Validate enforces the identifier bounds, a valid same-session SourceRange, an Algorithm bounded at 64 bytes, a positive SchemaVersion, an optional bounded SummaryModel, and a bounded IdempotencyKey.

type CheckpointRecord

type CheckpointRecord struct {
	ID              CheckpointID    `json:"id"`
	Revision        Revision        `json:"revision"`
	Binding         BindingRevision `json:"binding"`
	SourceRange     SourceRange     `json:"source_range"`
	ActiveContext   []byte          `json:"active_context"`
	SummaryMetadata []byte          `json:"summary_metadata"`
	TurnID          uint64          `json:"turn_id"`
	Complete        bool            `json:"complete"`
}

CheckpointRecord is one committed state of a session with the CLI's summary metadata column and Complete flag. Distinct from the SDK's Checkpoint because storage persists Complete and SummaryMetadata.

func (CheckpointRecord) Validate

func (c CheckpointRecord) Validate() error

Validate enforces a valid ID, a valid Binding, a SourceRange that matches the ID, a non-empty ActiveContext within the CheckpointBytes bound, a SummaryMetadata within the EffectiveCheckpointMetadataLimit, a positive TurnID, and that ActiveContext + SummaryMetadata fit the overall checkpoint bound.

type CommitRequest

type CommitRequest struct {
	OperationID       string           `json:"operation_id"`
	Principal         Principal        `json:"principal"`
	SessionID         string           `json:"session_id"`
	Expected          Revision         `json:"expected"`
	ExpectedBinding   BindingRevision  `json:"expected_binding"`
	NewSourceEvents   []SourceEvent    `json:"new_source_events"`
	Payloads          []PayloadRecord  `json:"payloads,omitempty"`
	Checkpoint        CheckpointRecord `json:"checkpoint"`
	ActiveContext     []byte           `json:"active_context"`
	NewSession        uint64           `json:"new_session"`
	NewDurable        uint64           `json:"new_durable"`
	NewSourceSequence uint64           `json:"new_source_sequence"`
	NewBinding        BindingRevision  `json:"new_binding"`
	TurnID            uint64           `json:"turn_id"`
	BaseDigest        string           `json:"base_digest"`
	Fingerprint       string           `json:"fingerprint"`
	WorktreeInstance  WorktreeInstance `json:"worktree_instance,omitempty"`
}

func NewCommitRequest

func NewCommitRequest(principal Principal, sessionID string, expected Revision, expectedBinding BindingRevision, events []SourceEvent, checkpoint CheckpointRecord, activeContext []byte, newBinding BindingRevision, turnID uint64) (CommitRequest, error)

func (CommitRequest) Validate

func (r CommitRequest) Validate() error

type ContentRef

type ContentRef struct {
	Ref         string `json:"ref"`
	Namespace   string `json:"namespace"`
	SHA256      string `json:"sha256"`
	WorkspaceID string `json:"workspace_id"`
	SessionID   string `json:"session_id"`
	SubjectID   string `json:"subject_id"`
	Size        int    `json:"size"`
}

ContentRef is the CLI's durable address of one shared context blob. The SDK's ContentRef enforces Ref == HashPrefix+SHA256; the CLI uses "ctxp_<hex>" refs derived from the owner tuple so two principals with identical content do not collide on a global primary key. The two types share their field set and stay as two distinct Go types so the local validator can enforce the CLI's namespace.

func (ContentRef) Validate

func (r ContentRef) Validate() error

Validate bounds the Ref, enforces the CLI namespace, requires a lowercase 64-character SHA-256, bounds the owner strings, and rejects a negative Size. The validator refuses any "sha256:"-prefixed Ref so an SDK reference cannot pass through the CLI's storage.

type CutoverState

type CutoverState struct {
	Mode            string `json:"mode"`
	LegacySessionID string `json:"legacy_session_id"`
	SessionID       string `json:"session_id"`
}

type DeleteResult

type DeleteResult struct {
	SessionID         string   `json:"session_id"`
	TombstoneRevision Revision `json:"tombstone_revision"`
	RevokedRefs       int      `json:"revoked_refs"`
	AuditID           string   `json:"audit_id"`
}

type EnsureSessionRequest

type EnsureSessionRequest struct {
	Principal Principal       `json:"principal"`
	Binding   BindingRevision `json:"binding"`
	// Dir and Worktree record where the live session lives. They are written
	// once with the session row and drive TUI session restore.
	Dir      string `json:"dir,omitempty"`
	Worktree string `json:"worktree,omitempty"`
	// WorktreeInstance binds this session to one physical managed worktree.
	WorktreeInstance WorktreeInstance `json:"worktree_instance,omitempty"`
}

type ExportResult

type ExportResult struct {
	SessionID string   `json:"session_id"`
	Revision  Revision `json:"revision"`
	Records   []byte   `json:"records"`
	Count     int      `json:"count"`
	AuditID   string   `json:"audit_id"`
}

type ImportResult

type ImportResult struct {
	SessionID        string          `json:"session_id"`
	SourceRange      SourceRange     `json:"source_range"`
	Revision         Revision        `json:"revision"`
	Imported         int             `json:"imported"`
	IdempotencyKey   string          `json:"idempotency_key"`
	Status           string          `json:"status"`
	SourceMap        []SourceMapping `json:"source_map"`
	Cutover          CutoverState    `json:"cutover"`
	Rollback         RollbackToken   `json:"rollback"`
	PartialArtifacts []ContentRef    `json:"partial_artifacts"`
	Warnings         []string        `json:"warnings"`
}

type LegacyImporter

type LegacyImporter interface {
	Import(context.Context, Principal, string, string) (ImportResult, error)
}

type Limits

type Limits struct {
	// SourceEventBytes is the payload CHUNK size for durable source events
	// ([context] max_source_event_bytes). 0 means use DefaultPayloadChunkBytes.
	// It is NOT a whole-payload reject bound: multi-chunk payloads of any size
	// reassemble under one content ref.
	SourceEventBytes int
	// CheckpointBytes bounds a checkpoint's serialized active context, which is
	// the conversation the provider is sent. Its natural ceiling is the model's
	// prompt budget, which the planner already enforces upstream.
	CheckpointBytes int
	// CommitEvents bounds how many messages one turn may publish.
	CommitEvents int
	// CommitEventBytes bounds the aggregate payload bytes of one turn.
	CommitEventBytes int
	// SessionStateBytes bounds a stored session's serialized message state.
	SessionStateBytes int
	// ExportBytes bounds a context export.
	ExportBytes int
	// SummaryMetadataBytes bounds the persisted summary envelope.
	// Zero (uncapped by default) means the host imposes no compiled-in ceiling
	// on model-generated summary content.
	SummaryMetadataBytes int
	// CheckpointMetadataBytes bounds the summary_metadata column within a
	// checkpoint record. Zero means uncapped.
	CheckpointMetadataBytes int
}

Limits is the single declaration of every durable context bound that scales with how much the user and the model actually said.

A ZERO field means UNCAPPED, and every field is zero by default. That is not an oversight, it is the lesson of the wedge this file exists to prevent: the bounds used to be compiled-in constants (32 KiB of active context, 64 KiB per message) sized for a demo, while the models this product ships against carry 200k-1M token windows and `read_file`, `grep` and `run_command` results are uncapped by default. The first turn in which the agent did real work therefore exceeded them, and because publication is one transaction the WHOLE commit was refused - no source events, no checkpoint, no operation row. An active context only grows, so that turn wedged the session permanently and history stopped persisting with no way back.

A durable bound must never be able to destroy work the agent already finished. Anything set here is a deliberate operator ceiling, chosen by someone who knows their storage, not a default this binary imposes.

Bounds that describe SHAPE rather than volume stay compiled in, because they are correctness invariants rather than capacity policy: identifier and reference lengths, the source-range span that keeps range arithmetic honest, and the host-authored metadata envelopes the summarizer produces.

func CurrentLimits

func CurrentLimits() Limits

CurrentLimits returns the installed ceilings, or the uncapped default.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits is the shipped policy: every volume bound uncapped.

type PayloadRecord

type PayloadRecord struct {
	Ref       ContentRef     `json:"ref"`
	Retention RetentionClass `json:"retention"`
	Revoked   bool           `json:"revoked"`
	Data      []byte         `json:"data,omitempty"`
}

PayloadRecord is one stored payload under its content address. The field set matches the SDK's, but Ref is the CLI's local ContentRef (sha256:-prefixed SDK references do not pass Validate), so the type stays local.

func (PayloadRecord) Validate

func (p PayloadRecord) Validate() error

Validate enforces a valid Ref, a non-empty Retention, and, when Data is present, a length and digest that match Ref.

type PolicySnapshot

type PolicySnapshot struct {
	SummaryEnabled      bool     `json:"summary_enabled"`
	RedactionConfigured bool     `json:"redaction_configured"`
	Provider            string   `json:"provider"`
	Model               string   `json:"model"`
	CredentialScope     string   `json:"credential_scope"`
	NetworkEnabled      bool     `json:"network_enabled"`
	EndpointAllowlist   []string `json:"endpoint_allowlist,omitempty"`
	PolicyDigest        string   `json:"policy_digest"`
}

PolicySnapshot exposes the resolved redaction and credential policy to the runtime so a UI surface can render the operator's ceiling.

type Principal

type Principal struct {
	WorkspaceID string `json:"workspace_id"`
	SessionID   string `json:"session_id"`
	SubjectID   string `json:"subject_id"`
	// contains filtered or unexported fields
}

Principal is the CLI's three-tuple identity plus a per-process capability secret. The SDK has no equivalent; storage persists the capability digest alongside the owner tuple so a second handle with matching strings cannot authorize reads or writes.

func NewPrincipal

func NewPrincipal(workspaceID, sessionID, subjectID string) (Principal, error)

NewPrincipal mints a Principal with a fresh capability secret.

func (Principal) CapabilityDigest

func (p Principal) CapabilityDigest() string

CapabilityDigest is the durable, non-reversible identity of this principal handle. Storage persists this digest alongside the owner tuple so a second handle with matching strings cannot authorize reads or writes.

func (Principal) IsBound

func (p Principal) IsBound() bool

IsBound reports whether the Principal carries a non-zero capability.

func (Principal) Validate

func (p Principal) Validate() error

Validate bounds the three owner strings at MaxIdentifierBytes.

type RedactionPolicy

type RedactionPolicy struct {
	Configured bool               `json:"configured"`
	Patterns   []string           `json:"patterns,omitempty"`
	KeyNames   []string           `json:"key_names,omitempty"`
	Classifier func([]byte) error `json:"-"`
	// Redactor replaces sensitive spans in a source payload. It is supplied by
	// the host so this package keeps one redaction implementation rather than
	// growing a second that drifts from it. Without one, a flagged payload is
	// stored as metadata only - never refused.
	Redactor func([]byte) []byte `json:"-"`
}

RedactionPolicy is host-owned classifier configuration. A policy with no configured classifier is intentionally treated as unconfigured.

func (RedactionPolicy) Classify

func (policy RedactionPolicy) Classify(data []byte) error

Classify applies the host-owned redaction rules without minting a content reference. Summary validation uses the same classifier before model output can be persisted or sent to a provider.

type RetentionClass

type RetentionClass string

RetentionClass labels how long a payload is kept.

const (
	// RetentionSession keeps a payload for the session's lifetime.
	RetentionSession RetentionClass = "session"
	// RetentionCompliance keeps a payload past session deletion.
	RetentionCompliance RetentionClass = "compliance"
)

type Revision

type Revision struct {
	Session uint64 `json:"session"`
	Durable uint64 `json:"durable"`
	Source  uint64 `json:"source"`
}

Revision is a session's three counters. It carries no Validate; the commit rules compare it as a whole.

func NewRevision

func NewRevision(session, durable, source uint64) Revision

NewRevision builds one Revision from its three counters.

func (Revision) Validate

func (r Revision) Validate() error

Validate always returns nil so Revision stays a value type callers can range over.

type RollbackToken

type RollbackToken struct {
	SessionID      string `json:"session_id"`
	IdempotencyKey string `json:"idempotency_key"`
	Digest         string `json:"digest"`
}

type SanitizedPayload

type SanitizedPayload struct {
	Ref             ContentRef     `json:"ref"`
	Bytes           []byte         `json:"bytes,omitempty"`
	HashOnly        bool           `json:"hash_only"`
	Dereferenceable bool           `json:"dereferenceable"`
	Revoked         bool           `json:"revoked"`
	Retention       RetentionClass `json:"retention"`
}

SanitizedPayload is the CLI's content-addressed payload shape with the redaction outcome attached.

func SanitizeSourcePayload

func SanitizeSourcePayload(ctx context.Context, principal Principal, data []byte, policy RedactionPolicy) (SanitizedPayload, error)

SanitizeSourcePayload is the host boundary before any context bytes are persisted. Unconfigured policies deliberately produce metadata only.

type SessionAdmission

type SessionAdmission struct {
	Agent  string   `json:"agent"`
	Digest string   `json:"digest"`
	Names  []string `json:"names"`
}

SessionAdmission is a named session's deferred-tool admission record (plan tools/05 D3). Names are the tools admitted into the surface; Agent and Digest identify the agent binding and tier split they were admitted against, so a resume against a changed split can drop them fail-closed.

type SessionAdmissionCatalog

type SessionAdmissionCatalog interface {
	SaveSessionAdmission(context.Context, Principal, string, SessionAdmission) error
	LoadSessionAdmission(context.Context, Principal, string) (SessionAdmission, error)
}

SessionAdmissionCatalog is the optional durable surface for admission records. A store that does not implement it simply resumes with no admitted tools, which is the fail-closed direction.

type SessionCatalog

type SessionCatalog interface {
	SaveSession(context.Context, Principal, string, []byte, string, string, int, int, int, SessionSaveOptions) error
	LoadSession(context.Context, Principal, string) ([]byte, SessionCatalogInfo, error)
	ListSessions(context.Context, Principal) ([]SessionCatalogInfo, error)
	DeleteSessionSnapshot(context.Context, Principal, string) error
	PruneSessionSnapshots(context.Context, Principal, []string) error
}

SessionCatalog is the durable user-facing transcript surface. It is optional on the low-level context Store so memory/test stores need not implement named persistence.

type SessionCatalogInfo

type SessionCatalogInfo struct {
	SessionID    string `json:"session_id,omitempty"`
	Title        string `json:"title,omitempty"`
	Name         string `json:"name"`
	Model        string `json:"model"`
	Provider     string `json:"provider"`
	CreatedAt    string `json:"created_at"`
	UpdatedAt    string `json:"updated_at"`
	TurnCount    int    `json:"turn_count"`
	TokenCount   int    `json:"token_count"`
	MessageCount int    `json:"message_count"`
	// Dir is the absolute directory the session was created or used in.
	Dir string `json:"dir,omitempty"`
	// Worktree is the mivia worktree name when Dir lies inside one.
	Worktree string `json:"worktree,omitempty"`
	// WorktreeRoute marks a route that starts a new session in a worktree.
	// It does not contain a chat transcript or model binding.
	WorktreeRoute bool `json:"worktree_route,omitempty"`
	// WorktreeInstance retains the exact managed worktree for picker actions.
	WorktreeInstance WorktreeInstance `json:"worktree_instance,omitempty"`
}

SessionCatalogInfo is the metadata exposed to user-facing session pickers. Messages remain opaque to this package and are carried as canonical bytes. Dir and Worktree record where the session lived; the TUI restores that directory when the session is opened.

type SessionFirstMessageSource

type SessionFirstMessageSource interface {
	FirstUserMessage(context.Context, Principal, string) (string, error)
}

SessionFirstMessageSource resolves the first user message of a live context session for display titling. It is optional: a store that does not implement it simply leaves sessions untitled. The lookup is subject-scoped, never capability-scoped, so stale-capability rows (older runs) can still be titled.

type SessionLeaseRenewer added in v0.1.2

type SessionLeaseRenewer interface {
	RenewLease(ctx context.Context, principal Principal, sessionID string) error
	ReleaseLease(ctx context.Context, principal Principal, sessionID string) error
}

SessionLeaseRenewer is the optional surface a live process uses to prove to ReclaimSession that it is still actively working a session, so a second process resuming the same session id cannot silently evict it mid-turn. RenewLease is scoped by capability_digest (not just subject) so a process whose capability was already reclaimed away cannot resurrect its own stale lease and block the process that legitimately took over.

ReleaseLease clears a lease this process is voluntarily giving up (a clean shutdown), so the NEXT resume of this same session id does not have to wait out the staleness TTL just because this process quit before its lease happened to expire on its own - without this, a heartbeat that had renewed even once looks "live" to ReclaimSession for the full TTL after the owning process is already gone, and an ordinary "quit, then resume" within that window is refused as ErrSessionLiveElsewhere even though nothing is actually still using the session.

type SessionLifecycle

type SessionLifecycle interface {
	DeleteSession(context.Context, Principal, string) (DeleteResult, error)
	ExportSession(context.Context, Principal, string) (ExportResult, error)
}

type SessionLiveError added in v0.1.2

type SessionLiveError struct {
	LeaseAge   time.Duration
	RetryAfter time.Duration
}

SessionLiveError is the typed refusal ReclaimSession returns while another process's lease is still fresh. It wraps ErrSessionLiveElsewhere and carries what the refused caller can act on: how old the holder's last heartbeat is, and how long until the lease expires and takeover succeeds. Without these two numbers the refusal is indistinguishable from "broken" - the user cannot tell a 10-second wait from a permanent failure.

func (*SessionLiveError) Error added in v0.1.2

func (e *SessionLiveError) Error() string

func (*SessionLiveError) Unwrap added in v0.1.2

func (e *SessionLiveError) Unwrap() error

type SessionReclaimer

type SessionReclaimer interface {
	ReclaimSession(context.Context, Principal, string) (Snapshot, error)
}

SessionReclaimer is the optional surface that lets a resumed process take over write ownership of an existing, non-tombstoned live context session. A session's owner capability is minted fresh and only ever held in the process that created it (Principal.capability), so a later process that legitimately knows the session's id - the same id LoadSession/ DeleteSessionSnapshot already accept scoped only to workspace+subject, with no capability check - is trusted to reclaim it for resumed commits. Without this, a resumed session can read its prior history but every subsequent turn commits under the resuming process's own, unrelated session id instead of updating the one the caller asked to resume.

type SessionSaveOptions

type SessionSaveOptions struct {
	Dir              string
	Worktree         string
	WorktreeInstance WorktreeInstance
	// SessionID declares the live context session this save projects ("id is
	// id, name is name"). The storage layer stamps chat_sessions.session_id
	// with it only when it matches the catalog name and a live row exists at
	// write time; every other shape keeps the row a plain snapshot copy.
	SessionID string
	// SessionRevision stamps the live session's session_revision as of this
	// save, so a later LoadSession can tell "nothing has advanced the head
	// since this snapshot was taken" (safe to serve when there is no
	// completed checkpoint) apart from "a clear or a commit happened after
	// this snapshot" (the snapshot is stale). Only stored when SessionID is
	// also stamped; nil means unknown (e.g. a plain named copy), and the
	// storage layer then treats the row conservatively, exactly as it did
	// before this field existed.
	SessionRevision *uint64
}

SessionSaveOptions carries the optional metadata written with a named session snapshot. The zero value is valid and records no directory.

type SessionTitleCatalog

type SessionTitleCatalog interface {
	SetSessionTitle(context.Context, Principal, string, string, WorktreeInstance) error
}

SessionTitleCatalog stores optional display metadata for a bound context session.

type Snapshot

type Snapshot struct {
	Revision   Revision         `json:"revision"`
	Binding    BindingRevision  `json:"binding"`
	Active     CheckpointRecord `json:"active"`
	Source     []SourceEvent    `json:"source"`
	Tombstoned bool             `json:"tombstoned"`
}

Snapshot is the read model of one session. Tombstoned marks a session that DeleteSession removed.

type SourceEvent

type SourceEvent struct {
	ID              SourceID `json:"id"`
	Kind            string   `json:"kind"`
	Role            string   `json:"role"`
	ToolCallID      string   `json:"tool_call_id,omitempty"`
	PayloadRef      string   `json:"payload_ref,omitempty"`
	Provenance      string   `json:"provenance"`
	RedactionStatus string   `json:"redaction_status"`
	Size            int      `json:"size"`
}

SourceEvent is one durable event in a session's source log. The field set matches the SDK's; the local Validate wraps ErrInvalidDTO so the CLI's failure mode does not move with an SDK release.

func (SourceEvent) Validate

func (e SourceEvent) Validate() error

Validate bounds the four required text fields at 256 bytes, the two optional fields when set, and rejects a negative Size.

type SourceID

type SourceID struct {
	SessionID string `json:"session_id"`
	Sequence  uint64 `json:"sequence"`
}

SourceID names one event: a session and a sequence number. The local Validate wraps ErrInvalidDTO so the CLI's failure mode does not move with an SDK release; the SDK's Validate wraps ErrInvalidRecord.

func NewSourceID

func NewSourceID(sessionID string, sequence uint64) (SourceID, error)

NewSourceID builds one SourceID and validates it.

func (SourceID) Validate

func (id SourceID) Validate() error

Validate bounds the identifier.

type SourceMapping

type SourceMapping struct {
	LegacyID    string   `json:"legacy_id"`
	SessionID   string   `json:"session_id"`
	SourceStart SourceID `json:"source_start"`
	SourceEnd   SourceID `json:"source_end"`
}

type SourceRange

type SourceRange struct {
	Start SourceID `json:"start"`
	End   SourceID `json:"end"`
}

SourceRange spans events of one session, inclusive of both ends.

func NewSourceRange

func NewSourceRange(start, end SourceID) (SourceRange, error)

NewSourceRange builds one SourceRange and validates it.

func (SourceRange) Validate

func (r SourceRange) Validate() error

Validate enforces one session, an ordered span, and a span under MaxSourceRangeEvents.

type SourceReader

type SourceReader interface {
	ReadRange(context.Context, Principal, SourceRange) ([]SourceEvent, error)
	ReadPayload(context.Context, Principal, ContentRef) (SanitizedPayload, error)
}

type ValidationError

type ValidationError struct {
	Field  string
	Reason string
}

ValidationError retains the field that made a DTO invalid while allowing callers to use errors.Is(err, ErrInvalidDTO).

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error renders the sentinel, the field, and the reason.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap reports the sentinel under every validation failure.

type WorktreeAdmissionCatalog

type WorktreeAdmissionCatalog interface {
	SaveWorktreeSessionAdmission(context.Context, Principal, string, SessionAdmission, WorktreeInstance) error
	LoadWorktreeSessionAdmission(context.Context, Principal, string, WorktreeInstance) (SessionAdmission, error)
}

type WorktreeInstance

type WorktreeInstance struct {
	Worktree string `json:"worktree"`
	ID       string `json:"id"`
}

WorktreeInstance identifies one lifetime of a managed worktree. The random ID prevents an old process from using a same-name replacement.

func (WorktreeInstance) IsZero

func (i WorktreeInstance) IsZero() bool

IsZero reports whether no managed worktree binding exists.

func (WorktreeInstance) Validate

func (i WorktreeInstance) Validate() error

Validate rejects a partial or unsafe worktree binding.

type WorktreeInstanceInfo

type WorktreeInstanceInfo struct {
	Instance      WorktreeInstance
	CanonicalPath string
	State         WorktreeInstanceState
}

WorktreeInstanceInfo is the catalog record for one physical worktree.

type WorktreeInstanceState

type WorktreeInstanceState string

WorktreeInstanceState is the durable lifecycle state of one instance.

const (
	WorktreeCreating WorktreeInstanceState = "creating"
	WorktreeActive   WorktreeInstanceState = "active"
	WorktreeDeleting WorktreeInstanceState = "deleting"
	WorktreeDeleted  WorktreeInstanceState = "deleted"
)

type WorktreeRouteCatalog

type WorktreeRouteCatalog interface {
	SaveWorktreeRoute(context.Context, Principal, string, string) error
	DeleteWorktreeRoute(context.Context, Principal, string) (int64, error)
}

WorktreeRouteCatalog stores launch routes for mivia-managed worktrees. A route is separate from a chat session because it has no model binding.

type WorktreeSessionCatalog

type WorktreeSessionCatalog interface {
	BeginWorktreeCreation(context.Context, Principal, WorktreeInstance, string) error
	RegisterWorktreeInstance(context.Context, Principal, WorktreeInstance, string) error
	AbandonWorktreeCreation(context.Context, Principal, WorktreeInstance) error
	BeginWorktreeDeletion(context.Context, Principal, WorktreeInstance) error
	DeleteWorktreeSessions(context.Context, Principal, WorktreeInstance) (int, error)
	LoadWorktreeSession(context.Context, Principal, string, WorktreeInstance) ([]byte, SessionCatalogInfo, error)
	ListWorktreeSessions(context.Context, Principal, WorktreeInstance) ([]SessionCatalogInfo, error)
	DeleteWorktreeSessionSnapshot(context.Context, Principal, string, WorktreeInstance) error
	PruneWorktreeSessionSnapshots(context.Context, Principal, []string, WorktreeInstance) error
}

WorktreeSessionCatalog controls a managed worktree session lifecycle. The caller supplies the immutable instance to prevent same-name reuse.

type WorktreeStore

type WorktreeStore interface {
	LoadWorktree(context.Context, Principal, string, WorktreeInstance) (Snapshot, error)
}

WorktreeStore is the optional scoped read surface for a managed worktree. Implementations reject a non-active or mismatched physical instance.

Jump to

Keyboard shortcuts

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