state

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: May 24, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package state provides manifest CRUD, locking, and session discovery.

Package state provides manifest CRUD, locking, and session discovery.

Index

Constants

View Source
const SchemaVersion = 1

SchemaVersion is the current PaneState/SessionState schema version. Hook payloads that load a SessionState with a different Version overwrite it; readers (the tracker) treat foreign-version state as `unknown`.

View Source
const StateJSONLMaxSize = 1 << 20 // 1 MiB

StateJSONLMaxSize is the rotation threshold for state.jsonl. When the file exceeds this size, the writer rotates it to state.jsonl.1 and starts a fresh log. One historical file is kept.

Variables

View Source
var (
	// ErrManifestExists is returned when Create finds an existing manifest.
	ErrManifestExists = errors.New("manifest already exists")
	// ErrManifestNotFound is returned when Delete targets a missing manifest.
	ErrManifestNotFound = errors.New("manifest not found")
)

Sentinel errors for manifest operations.

Functions

func AppendStateEvent

func AppendStateEvent(id string, ev StateEvent) error

AppendStateEvent appends to state.jsonl and rotates the file when it crosses StateJSONLMaxSize. The state root must already exist (the session-state directory is created lazily). Rotation drops the previous .1 file, so only one rolled file is retained on disk.

func InitStartingState

func InitStartingState(id string, agentsByRole map[string]string) error

InitStartingState seeds state.json with State="starting" / Activity="started" for each provided pane (roleName → agentName, e.g. {"primary": "codex"}). Skips any pane that already has a non-empty State so the agent's first hook (which fires moments later) is never overwritten. This eliminates the brief "unknown" the tracker would otherwise render between spawn and the SessionStart hook.

func IsValidPartyID

func IsValidPartyID(id string) bool

IsValidPartyID reports whether the given string is a valid party session ID.

func NowUTC

func NowUTC() string

NowUTC returns the current time in the format used by bash manifest helpers.

func SanitizeResumeID

func SanitizeResumeID(v string) string

SanitizeResumeID returns v unchanged when it's a safe identifier, or "" when it contains characters that could escape filesystem / glob contexts (path traversal, wildcards, NUL, etc.). Exported so downstream consumers share the single source of truth rather than carrying their own copy of the regex.

func SaveSessionState

func SaveSessionState(id string, ss *SessionState) error

SaveSessionState writes the state.json for a session atomically. The write is serialized via flock on a sibling lockfile.

func SessionStateDir

func SessionStateDir(root, id string) string

SessionStateDir returns <root>/<id>. Caller must ensure id is valid.

func SessionStateLockPath

func SessionStateLockPath(root, id string) string

SessionStateLockPath returns the flock path. We lock a sibling file rather than state.json itself so the atomic rename of state.json never races a still-held lock fd.

func SessionStateLogPath

func SessionStateLogPath(root, id string) string

SessionStateLogPath returns the state.jsonl path for the session.

func SessionStatePath

func SessionStatePath(root, id string) string

SessionStatePath returns the state.json path for the session.

func SortByMtime

func SortByMtime(manifests []Manifest, root string)

SortByMtime sorts manifests by file modification time, newest first.

func StateRoot

func StateRoot() string

StateRoot resolves the directory that holds per-session state. Honors $QUESTMASTER_STATE_ROOT first, then legacy $PARTY_STATE_ROOT, and falls back to ~/.questmaster-state. Returns the empty string when neither an override nor $HOME is set (caller treats that as a no-op condition).

func UpdateSessionState

func UpdateSessionState(id string, mutate func(*SessionState) bool) error

UpdateSessionState performs a locked read-modify-write. mutate runs against the freshly-loaded state inside the critical section. Returning false from mutate aborts the write (used when the freshly-read state no longer satisfies the precondition the caller checked optimistically).

This is the only safe way to apply tracker-side mutations like done → idle: a naive Load → mutate → Save races against hooks because the load happens outside the lock.

Types

type AgentManifest

type AgentManifest struct {
	Name     string `json:"name"`
	Role     string `json:"role"`
	CLI      string `json:"cli"`
	ResumeID string `json:"resume_id,omitempty"`
	Window   int    `json:"window"`
}

AgentManifest stores per-agent runtime state in the manifest.

type Manifest

type Manifest struct {
	PartyID     string          `json:"party_id"`
	CreatedAt   string          `json:"created_at,omitempty"`
	UpdatedAt   string          `json:"updated_at,omitempty"`
	Title       string          `json:"title,omitempty"`
	Cwd         string          `json:"cwd,omitempty"`
	WindowName  string          `json:"window_name,omitempty"`
	Agents      []AgentManifest `json:"agents,omitempty"`
	AgentPath   string          `json:"agent_path,omitempty"`
	SessionType string          `json:"session_type,omitempty"`
	Workers     []string        `json:"workers,omitempty"`

	// Extra preserves unknown fields written by bash helpers
	// (e.g. parent_session, initial_prompt).
	Extra map[string]json.RawMessage `json:"-"`
}

Manifest represents a party session's persisted state. JSON field names are stable for round-trip compatibility with existing manifests. Extra holds unknown fields to preserve fields this version does not interpret.

func (Manifest) ExtraString

func (m Manifest) ExtraString(key string) string

ExtraString reads a string value from the manifest's Extra map.

func (Manifest) MarshalJSON

func (m Manifest) MarshalJSON() ([]byte, error)

MarshalJSON merges typed fields with Extra to preserve unknown keys.

func (*Manifest) SetExtra

func (m *Manifest) SetExtra(key, value string)

SetExtra sets a string value in the manifest's Extra map.

func (*Manifest) UnmarshalJSON

func (m *Manifest) UnmarshalJSON(data []byte) error

UnmarshalJSON preserves unknown fields in Extra.

type PaneState

type PaneState struct {
	Role      string    `json:"role"`
	Agent     string    `json:"agent"`
	State     string    `json:"state"`
	Activity  string    `json:"activity"`
	Tool      string    `json:"tool,omitempty"`
	Seq       int64     `json:"seq"`
	LastEvent time.Time `json:"last_event"`
	LastKind  string    `json:"last_kind"`

	Recent      []string `json:"recent,omitempty"`
	SessionFile string   `json:"session_file,omitempty"`
	PiSessionID string   `json:"pi_session_id,omitempty"`
}

PaneState is the renderer-visible state for one role within a session. Pi-specific carry-through fields are populated only when Agent == "pi".

type SessionState

type SessionState struct {
	SessionID string               `json:"session_id"`
	Version   int                  `json:"version"`
	Panes     map[string]PaneState `json:"panes"`
	SeenAt    time.Time            `json:"seen_at"`
}

SessionState is the authoritative per-session state snapshot written by hooks and read by the tracker. One file per session at <state_root>/<session_id>/state.json.

func LoadSessionState

func LoadSessionState(id string) (*SessionState, error)

LoadSessionState reads the state.json for a session. Returns (nil, nil) if the file does not exist (the renderer should treat this as "unknown"). The state root is resolved from $QUESTMASTER_STATE_ROOT, legacy $PARTY_STATE_ROOT, or $HOME.

type StateEvent

type StateEvent struct {
	Ts       time.Time              `json:"ts"`
	Agent    string                 `json:"agent"`
	Role     string                 `json:"role,omitempty"`
	Action   string                 `json:"action"`
	State    string                 `json:"state,omitempty"`
	Activity string                 `json:"activity,omitempty"`
	Tool     string                 `json:"tool,omitempty"`
	Kind     string                 `json:"kind,omitempty"`
	Fields   map[string]interface{} `json:"fields,omitempty"`
}

StateEvent is one line of state.jsonl. Free-form fields beyond the fixed columns belong in Fields so consumers parsing the log don't have to keep up with a moving schema.

type Store

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

Store manages manifest files on disk with flock-based locking.

func NewStore

func NewStore(root string) (*Store, error)

NewStore creates a Store rooted at the given directory, creating it if needed.

func OpenStore

func OpenStore(root string) *Store

OpenStore opens a Store for read-only access without creating the directory. DiscoverSessions and other reads gracefully return empty results if the directory does not exist. Use NewStore for mutating operations that need the directory.

func (*Store) AddWorker

func (s *Store) AddWorker(partyID, workerID string) error

AddWorker adds a worker ID to the manifest's workers list (deduplicated).

func (*Store) Create

func (s *Store) Create(m Manifest) error

Create writes a new manifest. Returns an error if it already exists. Uses flock to prevent TOCTOU races between concurrent Create calls.

func (*Store) Delete

func (s *Store) Delete(partyID string) error

Delete removes a manifest file and its lock file.

func (*Store) DiscoverSessions

func (s *Store) DiscoverSessions() ([]Manifest, error)

DiscoverSessions returns all party session manifests found in the state root. Non-party files, lock files, and corrupt manifests are silently skipped.

func (*Store) GetWorkers

func (s *Store) GetWorkers(partyID string) ([]string, error)

GetWorkers returns the worker IDs from a manifest.

func (*Store) Read

func (s *Store) Read(partyID string) (Manifest, error)

Read loads a manifest by party ID.

func (*Store) RemoveWorker

func (s *Store) RemoveWorker(partyID, workerID string) error

RemoveWorker removes a worker ID from the manifest's workers list.

func (*Store) Root

func (s *Store) Root() string

Root returns the state directory path.

func (*Store) Update

func (s *Store) Update(partyID string, fn func(*Manifest)) error

Update applies a mutation function to an existing manifest under lock.

Jump to

Keyboard shortcuts

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