store

package
v0.10.5 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MaxCustomModels         = 100
	MaxCustomModelNameBytes = 64
	MaxCustomModelURLBytes  = 512
	// CustomModelKeyPrefix starts every custom model's key variable name.
	CustomModelKeyPrefix = "UAM_BYOM_"
)

Custom model limits: models, and bytes per field.

View Source
const (
	MaxHiddenModels     = 200
	MaxHiddenModelBytes = 128
)

Hidden model limits: IDs per provider, and bytes per ID.

View Source
const (
	WebSendSteer = "steer"
	WebSendQueue = "queue"
)

The values of WebSettings.SendDefault.

View Source
const CurrentSchemaVersion = 4
View Source
const (
	DefaultAgentName = "opencode"
)

UI defaults and bounds. normalize clamps/coerces out-of-range or unknown on-disk values so a hand-edited or corrupt config can never feed an invalid value downstream (F44).

View Source
const SurfaceWeb = "web"

SurfaceWeb marks records owned by the `uam web` service.

View Source
const WebTitleModelNone = "none"

WebTitleModelNone is the WebSettings.TitleModel value that opts a provider out of the Utility model, so UAM makes no AI call of its own for it.

Variables

View Source
var ErrReadOnly = errors.New("store: config loaded from a newer schema is read-only")

ErrReadOnly is returned by Save/Update when asked to write a config loaded from a newer on-disk schema. Refusing the write prevents an older binary from clobbering fields it does not understand (F33).

Functions

func DefaultPath

func DefaultPath() string

func Key

func Key(agent, id string) string

func PruneOld

func PruneOld(cfg *Config, maxAge time.Duration, exists func(string) bool)

PruneOld drops long-stale terminal records whose session is gone. Records owned by another surface are never pruned here: their liveness is not a terminal session host, so the probe cannot speak for them.

func ShortID

func ShortID(id string) string

func ValidCustomModel added in v0.9.0

func ValidCustomModel(m WebCustomModel) error

ValidCustomModel reports why m cannot be stored, or nil. BaseURL is an http or https URL without credentials, query or fragment, so no secret can ride in it.

func ValidCustomModels added in v0.9.0

func ValidCustomModels(list []WebCustomModel) error

ValidCustomModels reports why list cannot be stored, or nil: at most MaxCustomModels valid entries, each selection ID once, and one connection (base URL, wire API, key variable) per provider name.

func ValidHiddenModel added in v0.8.0

func ValidHiddenModel(id string) bool

ValidHiddenModel reports whether id can be stored as a hidden model ID: 1 to MaxHiddenModelBytes bytes of UTF-8 without control characters, as stored model IDs are checked.

func ValidProviderSessionID

func ValidProviderSessionID(id string) bool

ValidProviderSessionID is the schema-v3 provider identity grammar. Runtime discovery must use this same boundary so it cannot persist a value that a later load would reject by dropping the containing session record.

func ValidateProfile

func ValidateProfile(profile Profile) error

func ValidateProfileName

func ValidateProfileName(name string) error

func ValidateSessionProfileOverrides

func ValidateSessionProfileOverrides(overrides SessionProfileOverrides) error

Types

type Config

type Config struct {
	SchemaVersion  int                      `json:"schema_version"`
	DefaultAgent   string                   `json:"default_agent"`
	DefaultProfile string                   `json:"default_profile"`
	Profiles       map[string]Profile       `json:"profiles"`
	Sessions       map[string]SessionRecord `json:"sessions"`
	UI             UISettings               `json:"ui"`
	// WebProjects holds the web interface's Projects, keyed by Project ID.
	WebProjects map[string]WebProject `json:"web_projects,omitempty"`
	// WebSettings are the web interface's settings, shared by every browser.
	WebSettings WebSettings `json:"web_settings,omitzero"`

	// ReadOnly is set when the on-disk file declares a SchemaVersion newer than
	// this binary understands. The app must not write such a config (doing so
	// would drop fields it does not model), so Save/Update refuse it (F33). It
	// is in-memory only and never serialized.
	ReadOnly bool
	// contains filtered or unexported fields
}

func DefaultConfig

func DefaultConfig() Config

func (Config) MarshalJSON

func (c Config) MarshalJSON() ([]byte, error)

func (*Config) PutSession

func (c *Config) PutSession(key string, rec SessionRecord) bool

PutSession inserts or updates rec under key with a guard against the 8-char ShortID map key collapsing two distinct full IDs into one slot (F22). The short key carries only 32 bits of entropy, so two same-agent sessions can collide; without this guard the second write would silently clobber the first, orphaning a live session whose only handle is that record. It returns true on a successful write (no record, or the same full ID) and false (with a log) when an existing record under key carries a different non-empty full ID.

func (*Config) UnmarshalJSON

func (c *Config) UnmarshalJSON(data []byte) error

type Mode

type Mode string
const (
	ModeYolo Mode = "yolo"
	ModeSafe Mode = "safe"
)

type MousePolicy

type MousePolicy string
const (
	MousePolicyAuto MousePolicy = "auto"
	MousePolicyOn   MousePolicy = "on"
	MousePolicyOff  MousePolicy = "off"
)

type PRRecord

type PRRecord struct {
	URL         string    `json:"url"`
	Number      int       `json:"number"`
	LastStatus  string    `json:"last_status"`
	LastChecked time.Time `json:"last_checked"`
}

type Profile

type Profile struct {
	Provider        *string      `json:"provider,omitempty"`
	Mode            *Mode        `json:"mode,omitempty"`
	CommandAlias    *string      `json:"command_alias,omitempty"`
	Mouse           *MousePolicy `json:"mouse,omitempty"`
	ControlPrefix   *string      `json:"control_prefix,omitempty"`
	BackDetach      *bool        `json:"back_detach,omitempty"`
	ScrollbackLines *int         `json:"scrollback_lines,omitempty"`
	// contains filtered or unexported fields
}

func (Profile) MarshalJSON

func (p Profile) MarshalJSON() ([]byte, error)

func (*Profile) UnmarshalJSON

func (p *Profile) UnmarshalJSON(data []byte) error

type SessionExit

type SessionExit struct {
	SessionName       string
	ProviderSessionID string
	ExitCode          int
	UAMInitiated      bool
}

SessionExit describes how a provider process left its native session host. UAMInitiated is true only for an explicit UAM stop/restart request; terminal provider exits and externally delivered signals remain natural exits.

type SessionProfileOverrides

type SessionProfileOverrides struct {
	Mode            *Mode        `json:"mode,omitempty"`
	CommandAlias    *string      `json:"command_alias,omitempty"`
	Mouse           *MousePolicy `json:"mouse,omitempty"`
	ControlPrefix   *string      `json:"control_prefix,omitempty"`
	BackDetach      *bool        `json:"back_detach,omitempty"`
	ScrollbackLines *int         `json:"scrollback_lines,omitempty"`
	// contains filtered or unexported fields
}

func (SessionProfileOverrides) MarshalJSON

func (o SessionProfileOverrides) MarshalJSON() ([]byte, error)

func (*SessionProfileOverrides) UnmarshalJSON

func (o *SessionProfileOverrides) UnmarshalJSON(data []byte) error

type SessionRecord

type SessionRecord struct {
	ID           string `json:"id"`
	Agent        string `json:"agent"`
	CommandAlias string `json:"command_alias,omitempty"`
	Name         string `json:"name"`
	Prompt       string `json:"prompt,omitempty"`
	Mode         Mode   `json:"mode"`
	Workdir      string `json:"workdir"`
	// SessionName is the backend session name ("uam-<agent>-<id>"). The JSON
	// key keeps its historical "tmux_session" spelling so configs written by
	// tmux-backed releases load unchanged.
	SessionName string    `json:"tmux_session"`
	CreatedAt   time.Time `json:"created_at"`
	LastSeenAt  time.Time `json:"last_seen_at"`
	Pinned      bool      `json:"pinned"`
	Group       string    `json:"group"`
	SortIndex   int       `json:"sort_index"`
	Status      Status    `json:"status,omitempty"`
	// ProviderSessionID is the agent CLI's own session id, recorded when the
	// provider lets uam seed it at dispatch (e.g. claude --session-id). A
	// resume can then target the exact provider session instead of the
	// provider's "most recent" heuristic.
	ProviderSessionID string `json:"provider_session_id,omitempty"`
	// LastExitCode records the agent process's exit status from the most
	// recent close (-1 when it died on a signal). Pointer so records from
	// older schemas stay distinguishable from a real exit 0.
	LastExitCode     *int                     `json:"last_exit_code,omitempty"`
	PR               *PRRecord                `json:"pr,omitempty"`
	Profile          string                   `json:"profile,omitempty"`
	ProfileOverrides *SessionProfileOverrides `json:"profile_overrides,omitempty"`
	// Surface names the owner of a record. Empty means a terminal session host;
	// SurfaceWeb means the web service. Terminal commands never list, attach,
	// resume or prune records with a non-empty Surface, and the web service
	// only opens its own, so neither takes over the other's conversations.
	Surface string `json:"surface,omitempty"`
	// Web is the web service's durable state for a SurfaceWeb record.
	Web *WebState `json:"web,omitempty"`
	// contains filtered or unexported fields
}

func (SessionRecord) MarshalJSON

func (r SessionRecord) MarshalJSON() ([]byte, error)

func (*SessionRecord) UnmarshalJSON

func (r *SessionRecord) UnmarshalJSON(data []byte) error

type Status

type Status string

Status distinguishes records that should keep behaving as live sessions (StatusActive — recoverable on attach) from records the user deliberately retired (StatusClosedByUser).

const (
	StatusActive       Status = "active"
	StatusClosedByUser Status = "closed_by_user"
)

type Store

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

func Open

func Open(path string) (*Store, error)

func (*Store) Load

func (s *Store) Load() (Config, error)

func (*Store) MarkSessionClosed

func (s *Store) MarkSessionClosed(sessionName string, exitCode int) error

MarkSessionClosed flags the record whose backend session name matches as user-closed and records the agent's exit code. It is what a session host calls when its agent exits — the native replacement for the tmux session-closed hook driving `uam notify-closed`. Idempotent and a no-op when no record matches (e.g. uam already deleted it via `uam rm`).

func (*Store) Path

func (s *Store) Path() string

func (*Store) Save

func (s *Store) Save(cfg Config) error

func (*Store) SetSessionProbe

func (s *Store) SetSessionProbe(exists func(string) bool)

SetSessionProbe injects a callback that reports whether a backend session name is still live. Migration uses it to tell a reboot-survivor (live -> stays Active) apart from a user-stopped session (dead -> closed-by-user). When unset, migration conservatively keeps the legacy Active behavior (F07).

func (*Store) TryMarkSessionClosed

func (s *Store) TryMarkSessionClosed(sessionName string, exitCode int) (bool, error)

TryMarkSessionClosed is the compatibility entry point for older callers. It records an explicit UAM stop and returns whether a durable record matched.

func (*Store) TryRecordSessionExit

func (s *Store) TryRecordSessionExit(exit SessionExit) (bool, error)

TryRecordSessionExit records the provider's latest exit while preserving resumability for natural exits. Only an explicit UAM stop/restart retires the record into the closed-by-user group.

func (*Store) Update

func (s *Store) Update(fn func(*Config) error) error

type TurnTiming added in v0.8.0

type TurnTiming struct {
	ID         string    `json:"id"`
	UserItemID string    `json:"user_item_id,omitempty"`
	StartedAt  time.Time `json:"started_at"`
	EndedAt    time.Time `json:"ended_at,omitzero"`
	State      string    `json:"state"`
}

TurnTiming records foreground boundaries observed by the web service. An unfinished observation becomes unknown after its runtime connection is lost.

type UISettings

type UISettings struct {
	GroupByDir bool `json:"group_by_dir"`
	// Sort and PeekWidth are retained schema-v3 compatibility fields. They are
	// normalized and round-tripped even when the TUI exposes no direct control.
	Sort      string `json:"sort"`
	PeekWidth int    `json:"peek_width"`
}

type WebBadge added in v0.8.0

type WebBadge struct {
	Text  string `json:"text"`
	Color string `json:"color"`
}

WebBadge is a Project's badge: Text is two uppercase ASCII letters or digits, Color a palette key the browser maps to a colour.

type WebCustomModel added in v0.9.0

type WebCustomModel struct {
	Name        string `json:"name"`
	DisplayName string `json:"display_name,omitempty"`
	BaseURL     string `json:"base_url"`
	ModelID     string `json:"model_id"`
	// WireAPI is "", "completions" (the default) or "responses".
	WireAPI   string `json:"wire_api,omitempty"`
	APIKeyEnv string `json:"api_key_env"`
}

WebCustomModel is one OpenAI-compatible model. Name names its provider connection, and models with the same Name share BaseURL, WireAPI and APIKeyEnv; the selection ID is Name + "/" + ModelID.

type WebProject added in v0.8.0

type WebProject struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	Dir       string    `json:"dir"`
	CreatedAt time.Time `json:"created_at"`
	// LegacyDefaults are the per-Project Task defaults of versions before
	// WebSettings.TaskDefaults. Load reads them once, adopts the newest valid
	// ones as the setting when it is unset, and clears them, so the next save
	// drops the key. Nothing writes them.
	LegacyDefaults WebTaskDefaults `json:"defaults,omitzero"`
	// Badge is zero until the web service assigns one; it also replaces an
	// invalid one on load.
	Badge WebBadge `json:"badge,omitzero"`
	// contains filtered or unexported fields
}

WebProject is a directory the web interface groups Tasks under. There is at most one per Dir.

func (WebProject) MarshalJSON added in v0.8.0

func (p WebProject) MarshalJSON() ([]byte, error)

func (*WebProject) UnmarshalJSON added in v0.8.0

func (p *WebProject) UnmarshalJSON(data []byte) error

type WebSettings added in v0.8.0

type WebSettings struct {
	// SendDefault is what Enter does while a turn runs: WebSendSteer or
	// WebSendQueue. Empty or unrecognised values mean steer at runtime.
	SendDefault string `json:"send_default,omitempty"`
	// HiddenModels lists, by provider, the model IDs the browser does not
	// offer: sorted, without duplicates, at most MaxHiddenModels each. It is
	// a display preference, never a check on requests.
	HiddenModels map[string][]string `json:"hidden_models,omitempty"`
	// TitleModel maps a provider to its Utility model: the model UAM uses for
	// its own small AI jobs, such as titling new Tasks. The key keeps its
	// first name, title_model. A provider without an entry uses its cheapest
	// priced model; WebTitleModelNone opts it out (it keeps its own title).
	TitleModel map[string]string `json:"title_model,omitempty"`
	// CustomModels are the OpenAI-compatible models the owner brought
	// (BYOM), valid as ValidCustomModels checks. No key is stored: only the
	// name of the service environment variable that holds it.
	CustomModels []WebCustomModel `json:"custom_models,omitempty"`
	// TaskDefaults are the settings a new Task starts with; zero when unset.
	TaskDefaults WebTaskDefaults `json:"task_defaults,omitzero"`
	// contains filtered or unexported fields
}

WebSettings are the web interface's settings. The zero value writes no web_settings key.

func (WebSettings) MarshalJSON added in v0.8.0

func (w WebSettings) MarshalJSON() ([]byte, error)

func (*WebSettings) UnmarshalJSON added in v0.8.0

func (w *WebSettings) UnmarshalJSON(data []byte) error

type WebState added in v0.8.0

type WebState struct {
	// Turn is the last known session state (for example "working" or
	// "completed").
	Turn        string       `json:"turn,omitempty"`
	TurnTimings []TurnTiming `json:"turn_timings,omitempty"`
	// RequestID is the client-generated ID of the last prompt submission.
	RequestID string `json:"request_id,omitempty"`
	// RequestStatus is that submission's outcome: accepted, rejected or
	// uncertain.
	RequestStatus      string          `json:"request_status,omitempty"`
	CommandResult      json.RawMessage `json:"command_result,omitempty"`
	CommandSubmissions json.RawMessage `json:"command_submissions,omitempty"`
	UpdatedAt          time.Time       `json:"updated_at"`
	// Detail is a sanitized, short explanation of the state (usually an error).
	Detail string `json:"detail,omitempty"`
	// ProjectID is the WebProject the session (a Task) belongs to.
	ProjectID string `json:"project_id,omitempty"`
	// Model is the selected model ID; empty means the provider default.
	Model       string `json:"model,omitempty"`
	Effort      string `json:"effort,omitempty"`
	ContextSize string `json:"context_size,omitempty"`
	// Title is the provider-generated conversation title, sanitized and
	// bounded.
	Title string `json:"title,omitempty"`
	// Stage is the Task's lifecycle stage: "" (active), "settled" or
	// "archived". Records written before stages existed are active.
	Stage string `json:"stage,omitempty"`
	// SettledAt is when the Task was settled; zero unless it is settled, or
	// was settled before it was archived.
	SettledAt time.Time `json:"settled_at,omitzero"`
	// ArchivedAt is when the Task was archived.
	ArchivedAt time.Time `json:"archived_at,omitzero"`
	// TerminalSession is the ID of the terminal session record tied to the
	// same provider conversation when the Task was imported. That record
	// stays the terminal's.
	TerminalSession string `json:"terminal_session,omitempty"`
	// Imported marks a conversation created outside this web service.
	Imported bool `json:"imported,omitempty"`
	// contains filtered or unexported fields
}

WebState is the small durable part of a web session. Transcripts stay with the provider; only the last known turn state and the last prompt request outcome are kept so a restarted service can report them.

func (WebState) MarshalJSON added in v0.8.0

func (w WebState) MarshalJSON() ([]byte, error)

func (*WebState) UnmarshalJSON added in v0.8.0

func (w *WebState) UnmarshalJSON(data []byte) error

func (*WebState) Update added in v0.8.0

func (w *WebState) Update(v WebState)

Update sets every field this version models to v's and keeps the fields a newer uam wrote, so a writer never drops them.

type WebTaskDefaults added in v0.8.0

type WebTaskDefaults struct {
	Provider    string `json:"provider"`
	Model       string `json:"model"`
	Effort      string `json:"effort"`
	ContextSize string `json:"context_size"`
	Mode        string `json:"mode"`
}

WebTaskDefaults are the settings a new Task starts with, shared by every browser. ContextSize is "default" unless a tier is chosen; Mode is safe or yolo. The zero value means unset: the browser then starts from the provider's own defaults.

Jump to

Keyboard shortcuts

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