Help us shape the future of Go! Take the 2026 Go Developer Survey

session

package
v0.57.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	ThemeModeSystem = "system"
	ThemeModeLight  = "light"
	ThemeModeDark   = "dark"
)

UI theme modes. The mode is a machine-wide preference stored in the GLOBAL config DB: "system" follows the operating system, "light" and "dark" pin the interface regardless. Any other value (including an empty one) reads back as "system", so an unknown stored value can never leave the UI without a theme.

View Source
const (
	StatusRunning = "running"
	StatusFailed  = "failed"
)

Turn-status values stored on Session.Status. Empty means no turn is in flight (idle, or the last turn finished cleanly) and draws no dot.

View Source
const (
	IntentPending = "pending"
	IntentActive  = "active"
	IntentDone    = "done"
	IntentDropped = "dropped"
)

Intent-step statuses stored on IntentStep.Status. A step is pending until the agent starts it, active while it is being worked, done once it is finished, and dropped when it is no longer needed.

View Source
const (
	SearchProviderNative = "native"
	SearchProviderTavily = "tavily"
)

Search providers. Native is the built-in engine compiled into the binary; Tavily is a third-party API keyed by the user's own token. The value is stored as a string so future providers slot in without a schema change.

View Source
const (
	SearchBrowserNative = "native"
	SearchBrowserSafari = "safari"
	SearchBrowserChrome = "chrome"
)

Which native engine leads the chain. SearchBrowserNative uses the HTTP path alone; SearchBrowserSafari and SearchBrowserChrome drive a real browser first — Safari on macOS, Chrome or Edge on Windows, so the two are the same slot under the platform's own name. The empty string is the built-in ordering — the HTTP path in front with the browser behind it — and is also what an unknown value collapses to.

View Source
const (
	DefaultSearchFetchTopK = 4
	DefaultSearchPageChars = 6000

	MinSearchFetchTopK, MaxSearchFetchTopK = 1, 10
	MinSearchPageChars, MaxSearchPageChars = 1000, 20000
)

Bounds and defaults for the native deep-research knobs, matching the constants in internal/agent so a value that survives normalisation is always usable.

View Source
const MaskedAPIKey = "__SET__"

MaskedAPIKey is the sentinel the UI receives in place of a stored secret, and sends back unchanged to mean "keep the key you already have". Shared with the provider-config masking so both credential surfaces behave identically.

View Source
const OGXPlanNone = "none"

OGXPlanNone is the plan value the web side reports for an account that has signed in but holds no plan. It mirrors billing's PlanNone.

Variables

View Source
var ErrSessionDeleted = errors.New("session deleted")

ErrSessionDeleted marks a store write rejected because the session — or the message the write belongs to — was deleted underneath the caller. Deleting a session cascades to its messages and parts, so a running agent loop's next INSERT fails the foreign-key constraint instead of finding its parent.

Functions

func DeleteModelCapability added in v0.6.0

func DeleteModelCapability(database *db.DB, modelID string) error

DeleteModelCapability clears a model's cached capability so it is re-probed on next use. An empty modelID clears every cached capability. Used by the manual-refresh path.

func DeleteModelCatalog added in v0.39.0

func DeleteModelCatalog(database *db.DB, providerID string) error

DeleteModelCatalog removes a provider's persisted catalogue, so the picker falls back to the static list until the next refresh. Used when a provider is unregistered (its credentials were cleared), so a stale catalogue cannot outlive the account it came from. A missing row is not an error.

func DeleteModelPreference

func DeleteModelPreference(database *db.DB, id string, providerID string) error

DeleteModelPreference removes the one provider's preference for a model; the same id under another provider keeps its own row.

func DeleteOGXAccount added in v0.38.0

func DeleteOGXAccount(database *db.DB) error

DeleteOGXAccount removes the account link. The plan itself lives on the web side and is untouched; this only forgets the local connection.

func DeleteProviderConfig added in v0.53.1

func DeleteProviderConfig(database *db.DB, providerID string) error

DeleteProviderConfig removes a provider's stored credentials entirely — the only call that un-registers a vendor slot: upserting an empty row would leave the id in GetAllProviderConfigs and keep it surfaced in the settings list. Deleting an id with no row is a no-op, not an error — the outcome the caller wants already holds.

func FinishedNaturally added in v0.26.0

func FinishedNaturally(finish *string) bool

FinishedNaturally reports whether a finish reason means the model was done.

Only three are: the model saying it finished, and the user saying stop. Every other reason — none recorded at all, a request for tools that nothing ran, an error, the output cap — describes a turn that stopped without reaching an end, which is what makes it something to pick back up.

The default is deliberately "not finished". A finish reason this code has never seen is far more likely to be a provider spelling one of the failures its own way than a fourth kind of success, and the cost of the two mistakes is not symmetric: offering a resume that turns out to be unnecessary wastes a click, while withholding one strands the conversation.

func GetThemeMode added in v0.48.0

func GetThemeMode(database *db.DB) (string, error)

GetThemeMode returns the stored UI theme mode, defaulting to "system" when no row exists or the store is unavailable.

func LearnModelContextWindow added in v0.35.0

func LearnModelContextWindow(database *db.DB, modelID string, window int) error

LearnModelContextWindow records a context window discovered from a context-overflow error body, without disturbing whatever else is known about the model. Windows ≤ 0 (nothing parsed) are ignored. When no capability record exists yet, one is created carrying only the window — the image fields stay at their defaults until a probe writes them.

func NormalizeThemeMode added in v0.48.0

func NormalizeThemeMode(mode string) string

NormalizeThemeMode collapses a caller-supplied mode to one of the three known values, defaulting to "system".

func Now

func Now() int64

func SetModelCapability added in v0.6.0

func SetModelCapability(database *db.DB, c *ModelCapability) error

SetModelCapability upserts a probed capability record for a model.

The upsert MERGES rather than replaces: supports_images and probed_at are taken from the incoming record (an image probe is authoritative each time it runs), but a learned context window survives a write that carries none — otherwise the image probe (which always writes ContextWindow 0) would erase a window the loop learned from an overflow error. Only an explicit positive ContextWindow in the incoming record overwrites the stored one.

func SetModelCatalog added in v0.39.0

func SetModelCatalog(database *db.DB, providerID string, models []CatalogModel) error

SetModelCatalog replaces one provider's persisted catalogue with models. The delete-then-insert runs in a transaction so a reader never sees the catalogue half-written, and a model the endpoint stopped listing is dropped rather than lingering from a previous refresh. An empty models clears the provider.

func SetModelPreference

func SetModelPreference(database *db.DB, p *ModelPreference) error

SetModelPreference upserts a model preference into the database.

Keyed by (id, provider_id): the same model id can be served by two providers (glm-5.3-flash exists under both the OGX plan and a custom OpenAI-compatible endpoint), and each provider's enabled state is its own — a toggle on one must not overwrite the other's row.

func SetOGXAccount added in v0.38.0

func SetOGXAccount(database *db.DB, a *OGXAccount) error

SetOGXAccount stores the account link, replacing any previous one — reconnecting is how a user switches OG Lab accounts.

func SetProviderConfig added in v0.2.1

func SetProviderConfig(database *db.DB, c *ProviderConfig) error

SetProviderConfig upserts a provider config row.

func SetSearchConfig added in v0.8.0

func SetSearchConfig(database *db.DB, c *SearchConfig) error

SetSearchConfig upserts the singleton config row, normalising every field before persisting so an invalid client payload can never store a bad value.

func SetThemeMode added in v0.48.0

func SetThemeMode(database *db.DB, mode string) error

SetThemeMode upserts the singleton UI-preferences row.

func ValidIntentStatus added in v0.49.0

func ValidIntentStatus(s string) bool

ValidIntentStatus reports whether s is one of the recognised IntentStep statuses.

Types

type AgentPartData added in v0.51.0

type AgentPartData struct {
	Handle    string `json:"handle"`
	Direction string `json:"direction"` // "in" | "out"
	Seq       int64  `json:"seq"`
	Kind      string `json:"kind,omitempty"`
	Topic     string `json:"topic,omitempty"`
	Text      string `json:"text"`
	// To is the recipient handle for an outbound post; empty means everyone.
	To string `json:"to,omitempty"`
	// SessionID is the peer's session, so the UI can link to it.
	SessionID string `json:"sessionId,omitempty"`
}

AgentPartData is one board post on the transcript: written by this agent ("out") or delivered to it from a peer ("in"). The text is stored unfenced; every path that puts it in front of a model passes it through board.Render.

type CatalogModel added in v0.39.0

type CatalogModel struct {
	ID              string  `json:"id"`
	Name            string  `json:"name"`
	ProviderID      string  `json:"providerId"`
	ActiveByDefault bool    `json:"activeByDefault"`
	SupportsImages  bool    `json:"supportsImages"`
	ContextWindow   int     `json:"contextWindow"`
	MaxOutputTokens int     `json:"maxOutputTokens"`
	Collection      string  `json:"collection"`
	InputPricePerM  float64 `json:"inputPricePerM"`
	OutputPricePerM float64 `json:"outputPricePerM"`
	// Per-million USD cache rates the provider published; 0 when it publishes
	// none.
	CacheReadPricePerM  float64 `json:"cacheReadPricePerM"`
	CacheWritePricePerM float64 `json:"cacheWritePricePerM"`
	// Efforts and DefaultEffort are the reasoning levels the host reported for
	// the model (empty when it reported none).
	Efforts       []string `json:"efforts,omitempty"`
	DefaultEffort string   `json:"defaultEffort,omitempty"`
}

CatalogModel is one entry of a provider's persisted model catalogue: the curated list that provider last reported, kept so a fresh process can populate the picker before its first background refresh finishes. It carries the ModelInfo fields that survive a round trip — the Default flag is not stored because it is recomputed per render from the provider's own default.

func GetModelCatalog added in v0.39.0

func GetModelCatalog(database *db.DB) ([]CatalogModel, error)

GetModelCatalog returns every persisted catalogue entry, across all providers.

type Delivery added in v0.29.0

type Delivery struct {
	// DispatchedAt is when the StreamChat attempt that succeeded left for the
	// provider. Re-stamped on every attempt so that retry backoff, which can run
	// to seconds, never lands inside TTFTMs.
	DispatchedAt int64 `json:"dispatchedAt,omitempty"`
	// ConnectedAt is when the provider answered 200 and the stream opened. This
	// is the moment the request is known to have reached the model.
	ConnectedAt int64 `json:"connectedAt,omitempty"`
	// FirstTokenAt is when the first content event of any kind arrived — text,
	// reasoning or a tool call. Reasoning counts: on a thinking model it is what
	// arrives first, and waiting for text instead would report the whole
	// thinking phase as latency.
	FirstTokenAt int64 `json:"firstTokenAt,omitempty"`
	// TTFTMs is FirstTokenAt − DispatchedAt: time to first token, the model's
	// own queue and prefill, free of ogcode's prompt building and of backoff.
	TTFTMs int64 `json:"ttftMs,omitempty"`
	// QueuedMs is DispatchedAt − the prompt's CreatedAt: everything ogcode did
	// before the request left, which is prompt building, memory retrieval and
	// compaction. Kept apart from TTFTMs so a slow turn can be attributed.
	QueuedMs int64 `json:"queuedMs,omitempty"`
	// Attempts is how many StreamChat tries the connection took. Above 1 means
	// the stream was opened more than once before it held.
	Attempts int `json:"attempts,omitempty"`
	// FirstTokenKind is what opened the response: "text", "reasoning" or "tool".
	FirstTokenKind string `json:"firstTokenKind,omitempty"`
}

Delivery records how far a turn got on its way to the model, and how long the model took to say its first word.

It hangs off the assistant message rather than the prompt that caused it: the loop owns that record from the moment it creates it, and ParentID already points back at the prompt, so the pairing costs nothing. Only the first step of a turn can point at a human prompt — from step 2 on, the preceding user message is the tool-result message the loop wrote itself — so a client reading Delivery through ParentID never has to reason about steps.

type ImagePartData added in v0.14.0

type ImagePartData struct {
	MediaType string `json:"mediaType"`
	Data      string `json:"data"`
	Name      string `json:"name,omitempty"`
}

ImagePartData stores a user-uploaded image attachment. Data is base64-encoded image bytes; MediaType is e.g. "image/jpeg" or "image/png". The Name field carries the original filename (optional, for display only).

type IntentStep added in v0.49.0

type IntentStep struct {
	Step   string `json:"step"`
	Status string `json:"status"` // one of the Intent* constants
	// Description says what the step actually involves — the files, the library,
	// the command. It is never shown to the user: a matcher reads it to suggest
	// the skills and MCP tools likely to help with the step. Optional, so a plan
	// written before the field existed still parses.
	Description string `json:"description,omitempty"`
	// MatchedSkills and MatchedMCPs count what the turn's plan matcher paired
	// with this step: skills it could load, MCP servers whose tools it could
	// call. Zero means nothing matched — or that the step was never routed,
	// which is what a step with no description gets. Written by the agent loop
	// from the routing answer, never by the tool the agent calls.
	MatchedSkills int `json:"matchedSkills,omitempty"`
	MatchedMCPs   int `json:"matchedMCPs,omitempty"`
	// MatchedSkillNames and MatchedMCPNames name what those counts stand for, so
	// the plan chip can say which skill or MCP server a count is about on hover.
	// They are the routed, capped set — one entry per counted match, in the
	// order the matcher named them. Absent when nothing matched.
	MatchedSkillNames []string `json:"matchedSkillNames,omitempty"`
	MatchedMCPNames   []string `json:"matchedMCPNames,omitempty"`
}

IntentStep is one item of a SessionIntent.

type InterruptReason added in v0.26.0

type InterruptReason string

InterruptReason classifies why a turn stopped short.

const (
	// InterruptRateLimit is a 429 or a provider quota, the case where waiting
	// is the whole fix. RetryAfter carries when waiting is over, where the
	// provider said.
	InterruptRateLimit InterruptReason = "rate_limit"
	// InterruptServerError is a 5xx or an overloaded provider.
	InterruptServerError InterruptReason = "server_error"
	// InterruptNetwork is a connection that dropped, timed out or was refused.
	InterruptNetwork InterruptReason = "network"
	// InterruptAuth is a rejected key, an expired token, an exhausted balance —
	// resumable, but only once a human has fixed the account behind it.
	InterruptAuth InterruptReason = "auth"
	// InterruptContext is a request too large for the model's window that
	// compaction could not bring back under it.
	InterruptContext InterruptReason = "context"
	// InterruptModelCapability is a 400 because the model lacks a capability the
	// request used (e.g. image input). Resumable: resume strips the offending
	// content and retries, or the user switches to a model that has the capability.
	InterruptModelCapability InterruptReason = "model_capability"
	// InterruptCrashed marks a turn found unfinished at startup: the process
	// died mid-stream and never got to record anything about why.
	InterruptCrashed InterruptReason = "crashed"
	// InterruptStalled marks a turn that recorded a finish reason but not one
	// the model chose — it asked for a tool and nothing ran it, or it hit the
	// output cap mid-answer. The loop that would have carried on is gone.
	InterruptStalled InterruptReason = "stalled"
	// InterruptFatal is everything a retry cannot help — a malformed request, a
	// model that does not exist, a provider rejecting the tool schema.
	InterruptFatal InterruptReason = "fatal"
)

type Interruption added in v0.26.0

type Interruption struct {
	Reason    InterruptReason `json:"reason"`
	Resumable bool            `json:"resumable"`
	// Detail is a short human-facing sentence naming what to do about it. The
	// raw provider error stays in Error.
	Detail string `json:"detail,omitempty"`
	// RetryAfter is the unix second the provider said to come back at, or 0
	// where it said nothing. Only a rate limit tends to carry one.
	RetryAfter int64 `json:"retryAfter,omitempty"`
	// Step is the loop step the turn died on, for the UI to say how far it got.
	Step int `json:"step,omitempty"`
}

Interruption records why a turn stopped short of finishing and whether picking it up again is worth trying.

It sits beside Error rather than replacing it. Error is the provider's own words, which a user needs to read; this is the classification the resume path acts on, and the two answer different questions.

type MessageID

type MessageID = id.MessageID

func NewMessageID

func NewMessageID() MessageID

type MessageInfo

type MessageInfo struct {
	ID        MessageID    `json:"id"`
	SessionID SessionID    `json:"sessionId"`
	Role      MessageRole  `json:"role"`
	Agent     string       `json:"agent,omitempty"`
	ParentID  *MessageID   `json:"parentId,omitempty"`
	Finish    *string      `json:"finish,omitempty"`
	Cost      float64      `json:"cost,omitempty"`
	Tokens    *TokenCounts `json:"tokens,omitempty"`
	Error     *string      `json:"error,omitempty"`
	// Interrupted is set when a loop stopped part-way through this turn rather
	// than because the model finished. It is what a resume decides from.
	Interrupted *Interruption `json:"interrupted,omitempty"`
	// Delivery records how far this turn got on its way to the model and how
	// long the model took to start answering. Set on assistant messages only,
	// and nil on any written before the field existed.
	Delivery *Delivery `json:"delivery,omitempty"`
	// DisplayOnly marks a message that belongs in the transcript but must never
	// be sent to a model. Mid-loop guidance is the case it exists for: the text
	// is already folded into the running turn's user message, so persisting it
	// as an ordinary message would deliver it twice on the next request and
	// insert a second user row beside one the model is still answering — which
	// both APIs reject as a break in user/assistant alternation. Recording it
	// display-only keeps the user's own words in the conversation they typed
	// them into without changing what any model receives.
	DisplayOnly bool  `json:"displayOnly,omitempty"`
	CreatedAt   int64 `json:"createdAt"`
	// Model and Provider are the endpoint that produced an assistant message,
	// stamped when its step starts. A session can switch models between turns,
	// so the session's own Model says only what it runs on now; pricing a turn
	// needs the one that answered it. Empty on messages written before the
	// fields existed, which are priced at the session's model instead.
	Model    string `json:"model,omitempty"`
	Provider string `json:"provider,omitempty"`
}

func (*MessageInfo) CanResume added in v0.26.0

func (m *MessageInfo) CanResume() bool

CanResume reports whether a message is one a resume should act on.

type MessageRole

type MessageRole string
const (
	RoleUser      MessageRole = "user"
	RoleAssistant MessageRole = "assistant"
)

type MessageWithParts

type MessageWithParts struct {
	Info  MessageInfo `json:"info"`
	Parts []Part      `json:"parts"`
}

type ModelCapability added in v0.6.0

type ModelCapability struct {
	ModelID        string `json:"modelId"`
	SupportsImages bool   `json:"supportsImages"`
	ProbedAt       int64  `json:"probedAt"`
	ContextWindow  int    `json:"contextWindow"`
}

ModelCapability is a probed/known capability record for a model, persisted so the image-support probe runs at most once per model (until manually refreshed). ContextWindow carries a window learned from a context-overflow error body (0 = unknown); see session.LearnModelContextWindow.

func GetModelCapability added in v0.6.0

func GetModelCapability(database *db.DB, modelID string) (*ModelCapability, bool, error)

GetModelCapability returns the persisted capability record for a model. The second return value is false when no record exists (not yet probed).

type ModelPreference

type ModelPreference struct {
	ID          string `json:"id"`
	Enabled     bool   `json:"enabled"`
	ProviderID  string `json:"providerId"`
	DisplayName string `json:"displayName"`
	IsCustom    bool   `json:"isCustom"`
	// Collection is an optional group name for custom models so OpenAI-compatible
	// providers added through the OpenAI provider (Gemini, DeepSeek, Groq, …) can
	// be grouped together in the UI instead of all collapsing under "OpenAI".
	// Empty for built-in models and legacy custom models (falls back to providerId).
	Collection string `json:"collection"`
	// The rest is what the user told us about a custom model no catalogue knows.
	// Each zero value (or false) means "not configured", so that field falls
	// back to a catalogue or provider fact instead of overriding it.
	ContextWindow       int     `json:"contextWindow"`
	MaxOutputTokens     int     `json:"maxOutputTokens"`
	InputPricePerM      float64 `json:"inputPricePerM"`
	OutputPricePerM     float64 `json:"outputPricePerM"`
	CacheReadPricePerM  float64 `json:"cacheReadPricePerM"`
	CacheWritePricePerM float64 `json:"cacheWritePricePerM"`
	// SupportsImages can only ever force image support ON: a bool has no way to
	// say "yes" from "unconfigured", and catalogue facts outrank a false.
	SupportsImages bool  `json:"supportsImages"`
	CreatedAt      int64 `json:"createdAt"`
	UpdatedAt      int64 `json:"updatedAt"`
}

func GetModelPreferences

func GetModelPreferences(database *db.DB) ([]*ModelPreference, error)

GetModelPreferences returns all model preference overrides from the database.

type ModelPreferenceStore

type ModelPreferenceStore struct{}

type OGXAccount added in v0.38.0

type OGXAccount struct {
	Token         string `json:"token"`
	Email         string `json:"email"`
	Plan          string `json:"plan"`
	TimeConnected int64  `json:"timeConnected"`
}

OGXAccount is the stored link between this install and an OG Lab account holding an OGX plan. The token is the credential the web side handed back through the connect flow; email and plan are display fields for the settings screen. Kept in the global config DB — the plan follows the user, not the project.

func GetOGXAccount added in v0.38.0

func GetOGXAccount(database *db.DB) (*OGXAccount, error)

GetOGXAccount returns the stored account link, or nil when none has been connected. nil rather than a zero value: "no row" is the disconnected state, and callers branch on it.

func (*OGXAccount) HasPlan added in v0.38.0

func (a *OGXAccount) HasPlan() bool

HasPlan reports whether the linked account holds a plan that grants model access. A link without one is connected but unusable: the gateway would serve it an empty model catalogue. Callers use this to decide whether to register the OGX provider at all — registering a planless one would put it in ProviderPriority ahead of the other providers while it can serve nothing, and the user would land on a default that cannot answer.

A missing plan field is treated the same as "none": both mean no usable plan, and being wrong in that direction merely leaves the other providers in place.

type Part

type Part struct {
	ID        PartID          `json:"id"`
	MessageID MessageID       `json:"messageId"`
	SessionID SessionID       `json:"sessionId"`
	Type      PartType        `json:"type"`
	Data      json.RawMessage `json:"data"`
	CreatedAt int64           `json:"createdAt"`
	UpdatedAt int64           `json:"updatedAt"`
}

type PartID

type PartID = id.PartID

func NewPartID

func NewPartID() PartID

type PartType

type PartType string
const (
	PartText      PartType = "text"
	PartTool      PartType = "tool"
	PartReasoning PartType = "reasoning"
	PartFile      PartType = "file"
	PartImage     PartType = "image"
	PartAgent     PartType = "agent"
)

type PermissionID

type PermissionID = id.PermissionID

func NewPermissionID

func NewPermissionID() PermissionID

type ProviderConfig added in v0.2.1

type ProviderConfig struct {
	ProviderID string `json:"providerId"`
	APIKey     string `json:"apiKey"`
	BaseURL    string `json:"baseUrl"`
	UpdatedAt  int64  `json:"updatedAt"`
}

ProviderConfig holds credentials for a single LLM provider stored in the DB.

func GetAllProviderConfigs added in v0.2.1

func GetAllProviderConfigs(database *db.DB) ([]*ProviderConfig, error)

GetAllProviderConfigs returns stored configs for all providers.

func GetProviderConfig added in v0.2.1

func GetProviderConfig(database *db.DB, providerID string) (*ProviderConfig, error)

GetProviderConfig returns the stored config for a provider. Returns a zero-value config (empty fields) when no row exists.

func MaskedProviderConfig added in v0.2.1

func MaskedProviderConfig(c *ProviderConfig) *ProviderConfig

MaskedProviderConfig returns a copy with the API key replaced by a sentinel so it can be sent to the UI without leaking the real value.

type ReasoningPartData

type ReasoningPartData struct {
	Text      string `json:"text"`
	Signature string `json:"signature,omitempty"`
	// RedactedData is the opaque payload of an Anthropic redacted_thinking
	// block. Such a block has no readable text, and dropping it — or replaying
	// it as an ordinary thinking block — breaks the round-trip the API
	// requires within a tool-use turn.
	RedactedData string `json:"redactedData,omitempty"`
	// Model is the model that produced this block. Thinking blocks are tied to
	// the model that generated them: replayed to any other model they are
	// silently ignored but still billed as input, and an unsigned block from an
	// OpenAI-family model is rejected outright. Recording the origin lets the
	// conversion drop what the current model cannot use.
	Model string `json:"model,omitempty"`
}

type SearchConfig added in v0.8.0

type SearchConfig struct {
	Enabled bool `json:"enabled"`
	// Provider selects the search backend: "native" (default) or "tavily".
	Provider string `json:"provider"`
	// TavilyAPIKey is the token for the Tavily provider. Masked to MaskedAPIKey
	// on read so it never reaches the UI in the clear.
	TavilyAPIKey string `json:"tavilyApiKey"`
	// FetchTopK is how many ranked URLs the native engine reads in full before
	// synthesising an answer.
	FetchTopK int `json:"fetchTopK"`
	// PageChars is the per-page character cap fed into synthesis.
	PageChars int `json:"pageChars"`
	// Browser selects which native engine leads: "native", "safari" or
	// "chrome", or "" for the built-in ordering (HTTP first, the browser as the
	// fallback).
	Browser   string `json:"browser"`
	UpdatedAt int64  `json:"updatedAt"`
}

SearchConfig holds the global web-search toggle, the active search provider and its credential.

func GetSearchConfig added in v0.8.0

func GetSearchConfig(database *db.DB) (*SearchConfig, error)

GetSearchConfig returns the stored config. If no row exists it returns the defaults, which have search ENABLED: the backend is compiled into the binary and needs nothing installed, so there is no setup step to gate it behind. A user who does not want outbound requests turns the toggle off, and that stored choice is honoured on every later read.

func MaskedSearchConfig added in v0.32.0

func MaskedSearchConfig(c *SearchConfig) *SearchConfig

MaskedSearchConfig returns a copy with the Tavily key replaced by the mask sentinel so the config can be sent to the UI without leaking the real value. Mirrors MaskedProviderConfig.

type Session

type Session struct {
	ID                SessionID    `json:"id"`
	ProjectID         string       `json:"projectId"`
	Directory         string       `json:"directory"`
	Title             string       `json:"title"`
	Model             string       `json:"model,omitempty"`
	Provider          string       `json:"provider,omitempty"`
	Effort            string       `json:"effort,omitempty"` // reasoning effort the loop requests (internal/provider/effort.go); empty = the vendor's default
	SessionType       string       `json:"sessionType,omitempty"`
	Permission        string       `json:"permission,omitempty"`
	CompactionSummary string       `json:"compactionSummary,omitempty"`
	UtilityTokens     *TokenCounts `json:"utilityTokens,omitempty"`
	// Status is the session's in-flight turn state, shown as a dot in the
	// session list: StatusRunning while a turn is running, StatusFailed if the
	// last turn ended in an error, and "" (idle or finished cleanly) otherwise.
	Status string `json:"status,omitempty"`
	// Intent is the forward-looking plan for the current turn: the steps the
	// agent is working through, each with a status. Written by the update_intent
	// tool and cleared when the next turn starts, so it describes the turn in
	// flight rather than the session's history.
	Intent    *SessionIntent `json:"intent,omitempty"`
	CreatedAt int64          `json:"createdAt"`
	UpdatedAt int64          `json:"updatedAt"`
}

type SessionID

type SessionID = id.SessionID

func NewSessionID

func NewSessionID() SessionID

type SessionIntent added in v0.49.0

type SessionIntent struct {
	Steps []IntentStep `json:"steps"`
}

SessionIntent is the agent's forward-looking plan for the current turn: an ordered list of the steps it means to take, each with a status. The whole list is replaced on every update_intent call (last write wins), so it always describes what remains as the agent understands it now.

type StepUsage added in v0.40.0

type StepUsage struct {
	MessageID MessageID
	SessionID SessionID
	Model     string
	Provider  string
	Tokens    TokenCounts
	CreatedAt int64
}

StepUsage is what one assistant step spent and on which endpoint: the part of a message a cost view needs, read without the message's parts.

type Store

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

func NewStore

func NewStore(database *db.DB) *Store

func (*Store) AddUtilityUsage added in v0.39.0

func (s *Store) AddUtilityUsage(id SessionID, u TokenCounts) error

AddUtilityUsage adds the tokens a utility call spent to the session's running totals. Utility calls — title generation, command risk assessment, context compaction — stream their usage on a call the main-turn accounting never sees, so it accumulates here instead of on a message. The UPDATE increments rather than assigns, so concurrent calls cannot lose one another's tokens, and it leaves time_updated untouched: this is not a user-visible session change.

func (*Store) AnnouncedPorts added in v0.40.0

func (s *Store) AnnouncedPorts(directory string) ([]int, error)

AnnouncedPorts returns the ports announced in directory, sorted ascending. A missing or empty directory yields nothing, as does a nil store.

func (*Store) Create

func (s *Store) Create(session *Session) error

func (*Store) CreateMessage

func (s *Store) CreateMessage(msg *MessageInfo) error

func (*Store) CreatePart

func (s *Store) CreatePart(part *Part) error

func (*Store) DB added in v0.6.0

func (s *Store) DB() *db.DB

DB returns the underlying database handle, used by helpers that operate on other tables (e.g. model capability records).

func (*Store) Delete

func (s *Store) Delete(id SessionID) error

func (*Store) DeleteMessage added in v0.19.1

func (s *Store) DeleteMessage(messageID MessageID) error

DeleteMessage removes a message and all of its parts. Foreign-key cascade handles part deletion automatically. Used to clean up partial assistant messages left behind when mid-loop guidance cancels a text-only stream — keeping them would produce two consecutive assistant role messages on the next prompt, which the Anthropic and OpenAI APIs reject with a 400.

func (*Store) ForgetAnnouncedPort added in v0.40.0

func (s *Store) ForgetAnnouncedPort(port int) error

ForgetAnnouncedPort removes port from every directory's record, which takes its tile off the Preview grid and stops the proxy serving it. Forgetting a port that was never recorded is not an error.

func (*Store) Get

func (s *Store) Get(id SessionID) (*Session, error)

func (*Store) GetMessage

func (s *Store) GetMessage(messageID MessageID) (*MessageWithParts, error)

func (*Store) GetMessages

func (s *Store) GetMessages(sessionID SessionID, before MessageID, limit int) ([]*MessageWithParts, error)

func (*Store) GetMessagesPage added in v0.43.0

func (s *Store) GetMessagesPage(sessionID SessionID, before MessageID, limit int) ([]*MessageWithParts, bool, error)

GetMessagesPage is GetMessages for a paginating reader: the newest `limit` messages older than `before` (or overall, when before is empty), oldest first, plus whether anything older remains. It reads one row past the page rather than issuing a second query — the extra row's presence is the answer, and it is dropped before returning — so a page that happens to end exactly at the first message still reports that nothing is left.

func (*Store) GetPart

func (s *Store) GetPart(partID PartID) (*Part, error)

func (*Store) GetParts

func (s *Store) GetParts(messageID MessageID) ([]Part, error)

func (*Store) GetPartsForMessages added in v0.43.0

func (s *Store) GetPartsForMessages(ids []MessageID) (map[MessageID][]Part, error)

GetPartsForMessages loads the parts of many messages in a single query, grouped by message id and preserving each message's creation order. The ids are chunked so a long page never exceeds SQLite's bound-parameter limit (SQLITE_MAX_VARIABLE_NUMBER, 999 on older builds).

func (*Store) List

func (s *Store) List(directory string) ([]*Session, error)

func (*Store) ListAll added in v0.23.0

func (s *Store) ListAll() ([]*Session, error)

ListAll returns every session row regardless of directory or type, including the note/index/search sessions List hides. Agentic memory uses it to backfill project identity onto nodes written before that column existed.

func (*Store) ListStepUsage added in v0.40.0

func (s *Store) ListStepUsage(sessionID SessionID) ([]StepUsage, error)

ListStepUsage returns every assistant step that reported usage, oldest first — one session's when sessionID is set, the whole project's when it is empty. It decodes only the message rows, never their parts, so pricing a long session does not load its transcript.

func (*Store) PreviewPortPublished added in v0.40.0

func (s *Store) PreviewPortPublished(port int) (bool, error)

PreviewPortPublished reports whether port is recorded in any directory — the question the preview proxy asks before it forwards a request. A nil store publishes nothing, so a server without one refuses every preview rather than serving every port.

func (*Store) RecordAnnouncedPort added in v0.40.0

func (s *Store) RecordAnnouncedPort(directory string, port int) error

RecordAnnouncedPort records that a service on port was announced in directory. Re-announcing a port is idempotent: the first time wins, so a long-lived session records a service once. Nil-safe, so a caller without a store can record nothing rather than panicking.

func (*Store) ResetRunningStatuses added in v0.48.0

func (s *Store) ResetRunningStatuses() error

ResetRunningStatuses clears the running marker from every session. Called once at startup: a turn clears its own marker when it finishes, so a process that stopped mid-turn leaves the marker behind and the session list would show a running dot for a session nothing is running. (Two servers sharing one project DB would clear each other's markers; that is accepted.)

func (*Store) SetIntent added in v0.49.0

func (s *Store) SetIntent(id SessionID, intent *SessionIntent) error

SetIntent records the agent's plan for the current turn. A nil intent (or one with no steps) clears the column. It touches only that column and leaves time_updated alone, like SetStatus, so it cannot clobber a concurrent title or model change.

func (*Store) SetStatus added in v0.48.0

func (s *Store) SetStatus(id SessionID, status string) error

SetStatus records the session's in-flight turn state (StatusRunning, StatusFailed, or "" for idle/clean). It touches only that column, like AddUtilityUsage, so it cannot clobber a concurrent title or model change, and it leaves time_updated alone: this is not a user-visible session change.

func (*Store) Update

func (s *Store) Update(session *Session) error

Update rewrites the mutable, user-visible columns of a session. It deliberately leaves the utility_* columns alone: those are written only by AddUtilityUsage, which increments them, so a read-modify-write here would clobber an increment that landed between the caller's Get and this Update.

func (*Store) UpdateCompactionSummary

func (s *Store) UpdateCompactionSummary(id SessionID, summary string) error

UpdateCompactionSummary updates only the compaction_summary column for a session, avoiding the race condition of overwriting other fields (e.g., title, model) that may have changed concurrently.

func (*Store) UpdateMessage

func (s *Store) UpdateMessage(msg *MessageInfo) error

func (*Store) UpdatePart

func (s *Store) UpdatePart(part *Part) error

type TextPartData

type TextPartData struct {
	Text string `json:"text"`
}

type TokenCounts

type TokenCounts struct {
	Total      int `json:"total,omitempty"`
	Input      int `json:"input,omitempty"`
	Output     int `json:"output,omitempty"`
	Reasoning  int `json:"reasoning,omitempty"`
	CacheRead  int `json:"cacheRead,omitempty"`
	CacheWrite int `json:"cacheWrite,omitempty"`
}

func (TokenCounts) Consumed added in v0.40.0

func (t TokenCounts) Consumed() int

Consumed is the one definition of a total: every token the call(s) took in or produced — uncached input, both cache variants and output. It is what the providers themselves report as total_tokens, so a session's figure can be checked against their dashboards. Cache reads are included: they are processed and billed (at a discount) on every step that sends them. Reasoning is not added — it is billed inside output, and adding it would count it twice. Every surface that shows a total (per step, per session, utility, `ogcode run`, the web token pill's breakdown) must use this so they agree.

func (TokenCounts) Effective added in v0.40.0

func (t TokenCounts) Effective() int

Effective is Consumed without cache reads: the tokens spent fresh — uncached input, cache writes and output. Cache reads are the cheapest tokens, and on a long session they are most of Consumed, so a headline built on it mostly counts how often the history was re-sent. Cache writes stay in: they are new input, billed at or above the full input price. Reasoning is inside output, as in Consumed. The web token pill leads with this figure and `ogcode run` reports it beside Total; both must use this so they agree.

type ToolImage added in v0.6.0

type ToolImage struct {
	MediaType string `json:"mediaType"`
	Data      string `json:"data"`
}

ToolImage is an image produced by a tool, persisted so the model can be re-sent the image on history replay. Data is base64-encoded image bytes.

type ToolPartData

type ToolPartData struct {
	Tool   string `json:"tool"`
	CallID string `json:"callId"`
	// Extra is an opaque per-tool-call payload the host attached and requires
	// replayed verbatim on the assistant message (Gemini's
	// extra_content/thought_signature). Empty for every other host.
	Extra json.RawMessage `json:"extra,omitempty"`
	State ToolState       `json:"state"`
}

type ToolState

type ToolState struct {
	Status   ToolStatus      `json:"status"`
	Input    json.RawMessage `json:"input"`
	Output   *string         `json:"output,omitempty"`
	Error    *string         `json:"error,omitempty"`
	Title    *string         `json:"title,omitempty"`
	Metadata json.RawMessage `json:"metadata,omitempty"`
	Image    *ToolImage      `json:"image,omitempty"`
	Time     ToolTime        `json:"time"`
}

type ToolStatus

type ToolStatus string
const (
	ToolPending   ToolStatus = "pending"
	ToolRunning   ToolStatus = "running"
	ToolCompleted ToolStatus = "completed"
	ToolError     ToolStatus = "error"
	ToolDenied    ToolStatus = "denied"
)

type ToolTime

type ToolTime struct {
	Start int64 `json:"start,omitempty"`
	End   int64 `json:"end,omitempty"`
}

Jump to

Keyboard shortcuts

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