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

xray

package
v2.4.4 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	DefaultMCPListMemoryMaxLimit   = 20
	DefaultMCPSearchMemoryMaxLimit = 10
	DefaultMCPRecentMemoryMaxLimit = 20
)

DefaultMCPListMemoryMaxLimit / DefaultMCPSearchMemoryMaxLimit are the fallback hard result-count ceilings for list_memory/search_memory when the caller (main.go) doesn't override them via --mcp-list-memory-max-limit / --mcp-search-memory-max-limit. Applied regardless of what limit the MCP caller requests — a misbehaving or adversarial client (the limit is fully caller-controlled otherwise) cannot force a full-store dump (ADR-051).

View Source
const (
	// OriginObserved: written by code from facts.
	OriginObserved = memstore.OriginObserved
	// OriginDerived: written by a model. A prior claim, never evidence. A cell
	// with no origin is derived.
	OriginDerived = memstore.OriginDerived
	// OriginHuman: written by an identified person. Outranks derived.
	OriginHuman = memstore.OriginHuman
	// OriginVerified: derived, then passed a check. Set only in code.
	OriginVerified = memstore.OriginVerified
)

Origin values stored in MemoryCellSpec.Origin: who a cell came from.

View Source
const SystemConfigCRName = "xray-system-configuration"

Variables

This section is empty.

Functions

func MCPSigningSeed

func MCPSigningSeed(secrets ...string) []byte

MCPSigningSeed is the exported entry point for mcpSigningSeed, called from main.go with the same OAuth client secrets fed to sessionHMACKey (ADR-050).

func NewMCPAuthorizer

func NewMCPAuthorizer(issuer, resource string, tokenTTL time.Duration, dev bool, writeAllowed []string, seed []byte, logger zerolog.Logger) (*mcpAuthorizer, error)

NewMCPAuthorizer builds the co-located OAuth 2.1 authorization server for MCP. issuer is the public base URL; resource is the MCP endpoint URL (issuer+"/mcp" by convention). dev=true auto-issues tokens for a synthetic user so the flow is testable without a real IdP. seed deterministically derives the Ed25519 signing key so every replica converges on the same key (ADR-050); pass mcpSigningSeed(...) in production. A nil seed generates a random per-process key (single-replica dev). The returned *mcpAuthorizer is passed to both NewMCPServer (for token validation) and Server.SetMCPAuth (for the endpoints).

func NewMCPServer

func NewMCPServer(memory *MemoryStore, logger zerolog.Logger, getToken func() string, auth *mcpAuthorizer, syncer *CellSyncer, namespace string, memMapsDeps *msnap.Deps, listMemoryMaxLimit, searchMemoryMaxLimit, recentMemoryMaxLimit int) http.Handler

NewMCPServer returns an HTTP handler exposing memory tools. syncer and namespace are optional: if syncer is non-nil, upsert/delete also persist changes to AwarenessMemoryCell CRs in the cluster. listMemoryMaxLimit/searchMemoryMaxLimit/recentMemoryMaxLimit are hard result-count ceilings for list_memory/search_memory/recent_memory (ADR-051); pass <= 0 to use the Default* constants.

Authentication accepts two credential types, tried in order:

  1. an X-Ray-issued OAuth JWT (when auth != nil) — carries per-user scopes;
  2. the static shared Bearer secret from getToken() — treated as full scope.

At least one must be configured; otherwise the endpoint fails closed (503).

func SetOAuthClientPersistence

func SetOAuthClientPersistence(onChange func())

SetOAuthClientPersistence wires the flush hook so a new/updated registration is persisted to CR status on the next flush tick. Called once at startup, BEFORE mgr.Start, so the first CR replay restores into this store. A nil hook leaves the registry in-memory only (local dev without k8s).

func SetSessionPersistence

func SetSessionPersistence(hmacKey []byte, onChange func())

SetSessionPersistence enables CR-backed session persistence: tokens are hashed with hmacKey (derived from a stable secret so it survives restarts and matches across replicas) and onChange is invoked after each login/logout to flush the set to status. Called once at startup from main.go; a nil hmacKey leaves sessions in-memory only. See ADR-047.

func SetTrustedProxyHops

func SetTrustedProxyHops(n int)

SetTrustedProxyHops configures how many trusted reverse proxies sit in front of the server (e.g. 1 for a single ingress/LB that appends the client IP). Negative values are clamped to 0.

func ValidateSearchKind

func ValidateSearchKind(kind string) string

ValidateSearchKind rejects a kind filter that names no searchable kind. An empty filter means every kind and is valid.

func ValidateWrite

func ValidateWrite(kind, confidence string) string

ValidateWrite checks what an external writer may put in an item: a kind from the vocabulary, and high confidence on a stable_fact. "" means valid.

func WithOrigin

func WithOrigin(ctx context.Context, origin string) context.Context

WithOrigin returns ctx carrying the provenance of the caller. Upsert reads it: a write made as OriginHuman is never replaced by a derived one.

Types

type AzureOAuthConfig

type AzureOAuthConfig struct {
	ClientID     string
	ClientSecret string
	TenantID     string
	PublicURL    string
	// UICallbackOrigins allowlists origins a login may hand the session token
	// to via ?ui=, for a UI served from a different host than this server.
	// Empty (the default) disables the hand-off entirely — see resolveUIOrigin.
	UICallbackOrigins []string
}

AzureOAuthConfig holds the Azure AD OAuth app credentials.

type CategoryConfig

type CategoryConfig struct {
	ID    string  `json:"id"`
	Label string  `json:"label"`
	Color string  `json:"color"`
	Kind  string  `json:"kind"`
	Tag   string  `json:"tag,omitempty"`
	Tier  float64 `json:"tier,omitempty"`
	Angle float64 `json:"angle,omitempty"`
	Icon  string  `json:"icon,omitempty"`
	Scope string  `json:"scope,omitempty"`
}

CategoryConfig is the parsed, typed form of mcell.CategoryConfig — a UI display category served pre-auth via /v1/categories. JSON tags match the frontend's RuntimeCategoryConfig shape (ui/lib/category-config.ts). An unset tier or angle is left out: the frontend fills its defaults with ??, which keeps a 0 and would draw every such region at the centre.

type CellSyncer

type CellSyncer struct {
	client.Client
	// contains filtered or unexported fields
}

CellSyncer loads MemoryCell CRs into the MemoryStore and its vector index at startup and follows them as they change.

func NewCellSyncer

func NewCellSyncer(c client.Client, memory *MemoryStore, logger zerolog.Logger) *CellSyncer

func (*CellSyncer) Persist

func (s *CellSyncer) Persist(ctx context.Context, it *MemoryItem, namespace string) error

Persist writes a MemoryItem to the cluster as an MemoryCell CR. Preserves any existing embedding so the reconciler won't re-embed unnecessarily.

func (*CellSyncer) Reconcile

func (s *CellSyncer) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

Reconcile handles create/update/delete of MemoryCell CRs.

func (*CellSyncer) Remove

func (s *CellSyncer) Remove(ctx context.Context, id, namespace string) error

Remove deletes an MemoryCell CR by item ID.

func (*CellSyncer) SeedAll

func (s *CellSyncer) SeedAll(ctx context.Context, namespace string) error

SeedAll lists all MemoryCell CRs in the namespace and loads them into the store and its vector index. Called once at startup before serving requests.

func (*CellSyncer) SetMapRegistrar

func (s *CellSyncer) SetMapRegistrar(r MapRegistrar)

SetMapRegistrar wires the recall registry so graph-carrying cells (Data["graph"]) are loaded as searchable in-memory maps. No-op transport when nil.

func (*CellSyncer) SetRegistryPullSecret

func (s *CellSyncer) SetRegistryPullSecret(name string)

SetRegistryPullSecret names the Secret whose registry credentials are used to pull graphRef artifacts.

func (*CellSyncer) SetupWithManager

func (s *CellSyncer) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager registers the CellSyncer as a controller.

func (*CellSyncer) StartRetentionSweep added in v2.3.0

func (s *CellSyncer) StartRetentionSweep(ctx context.Context, namespace string, every time.Duration)

StartRetentionSweep is the backstop of deletion at the deadline: every `every` it deletes any cell past its StaleAfter that the deadline may delete, across all non-exempt kinds, in case a reconcile that should have fired at the deadline did not. A no-op when every <= 0. ctx bounds it.

func (*CellSyncer) SyncCell

func (s *CellSyncer) SyncCell(ctx context.Context, cell *mcell.MemoryCell) error

SyncCell reconciles a single MemoryCell into the in-memory store.

type ConfigWatcher

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

ConfigWatcher watches the single system_configuration MemoryCell CR by name and keeps a parsed VoiceSessionConfig atomically up to date. It is the only place that reads voice config — no List, no tag parsing.

func NewConfigWatcher

func NewConfigWatcher(memory *MemoryStore) *ConfigWatcher

NewConfigWatcher creates a ConfigWatcher that restores persisted stats into memory when the system_configuration CR is first loaded.

func (*ConfigWatcher) EventHandlerFuncs

func (w *ConfigWatcher) EventHandlerFuncs() toolscache.ResourceEventHandlerFuncs

EventHandlerFuncs returns handler funcs to register on the MemoryCell informer.

func (*ConfigWatcher) Get

Get returns the current config. Never nil — returns an empty config if not yet loaded.

type ConsolidationGroup

type ConsolidationGroup struct {
	Kind       string    `json:"kind"`
	Keep       string    `json:"keep"`
	KeepTitle  string    `json:"keepTitle"`
	Superseded []string  `json:"superseded"`
	Scores     []float64 `json:"scores"`
}

ConsolidationGroup is one near-duplicate cluster found by ConsolidateMemory: Keep is the id a reader should trust going forward, Superseded lists every other member's id (parallel to Scores, each member's score against Keep).

type ConsolidationReport

type ConsolidationReport struct {
	Scanned    int                  `json:"scanned"`
	Groups     []ConsolidationGroup `json:"groups"`
	Superseded int                  `json:"superseded"`
	Applied    bool                 `json:"applied"`
}

ConsolidationReport is the outcome of one ConsolidateMemory sweep.

type GitHubOAuthConfig

type GitHubOAuthConfig struct {
	ClientID     string
	ClientSecret string
	Org          string
	Team         string // optional — if set, user must be an active team member
	PublicURL    string // externally reachable base URL, e.g. https://xray.example.com
	// UICallbackOrigins allowlists origins a login may hand the session token
	// to via ?ui=, for a UI served from a different host than this server.
	// Empty (the default) disables the hand-off entirely — see resolveUIOrigin.
	UICallbackOrigins []string
}

GitHubOAuthConfig holds the OAuth App credentials and org restriction.

type GoogleOAuthConfig added in v2.4.0

type GoogleOAuthConfig struct {
	ClientID     string
	ClientSecret string
	// Domain is the Google Workspace domain an account must belong to, checked
	// against the ID token's hd claim. Empty admits any Google account.
	Domain    string
	PublicURL string
	// UICallbackOrigins allowlists origins a login may hand the session token
	// to via ?ui= (see resolveUIOrigin). Empty disables the hand-off.
	UICallbackOrigins []string
}

GoogleOAuthConfig holds the Google OAuth client credentials.

type GraphFetcher

type GraphFetcher func(ctx context.Context, ref name.Digest, kc authn.Keychain) ([]byte, error)

GraphFetcher pulls a graph artifact. Nil on a CellSyncer means the registry client below; a test injects an in-process one.

type MCPServerRef

type MCPServerRef struct {
	Name            string
	URL             string
	TokenSecretName string   // k8s Secret name
	TokenSecretKey  string   // key within the Secret; defaults to "token"
	Token           string   // resolved from SecretStore at session start; never persisted
	Tools           []string // allowlist of tool names; empty = all
}

MCPServerRef describes a remote MCP server to connect to at session start. Token is resolved at session start from the SecretStore, not stored in the CR.

type MapRegistrar

type MapRegistrar interface {
	AddSnapshotBytes(ctx context.Context, b []byte) error
	RemoveSnapshotByLabel(label string)
}

MapRegistrar registers/de-registers a mental-map graph carried in a MemoryCell as an in-memory snapshot. Satisfied by *recall/engine.Registry. Optional on the CellSyncer — nil when the server runs without a recall registry (keyword mode, or --mental-maps-dir unset).

type MemoryChangeEvent

type MemoryChangeEvent = hub.ChangeEvent

MemoryHitEvent and MemoryChangeEvent are the payloads the live stream carries.

type MemoryHitEvent

type MemoryHitEvent = hub.HitEvent

MemoryHitEvent and MemoryChangeEvent are the payloads the live stream carries.

type MemoryItem

type MemoryItem struct {
	ID         string   `json:"id"`
	Kind       string   `json:"kind"`
	Scope      string   `json:"scope"`
	Subject    string   `json:"subject,omitempty"`
	Title      string   `json:"title"`
	Content    string   `json:"content"`
	Confidence string   `json:"confidence,omitempty"`
	Tags       []string `json:"tags,omitempty"`
	Status     string   `json:"status"`
	// EventDate is only set for kind=event items. RFC3339.
	EventDate string `json:"eventDate,omitempty"`
	// Data holds arbitrary named blobs, e.g. Data["image"] for thumbnails.
	Data map[string]mcell.DataEntry `json:"data,omitempty"`
	// ImageURL is set server-side to /v1/memory/{id}/image when Data["image"] is present.
	ImageURL string `json:"imageUrl,omitempty"`
	// StaleAfter is the item's expiry deadline (decay by default). nil means no
	// deadline: the item never expires on its own.
	StaleAfter *time.Time `json:"staleAfter,omitempty"`
	CreatedAt  time.Time  `json:"createdAt"`
	UpdatedAt  time.Time  `json:"updatedAt"`
	// Links are the item's ThoughtLinks.
	Links []mcell.ThoughtLink `json:"links,omitempty"`
	// Origin is the cell's provenance: observed, derived, human or verified. A
	// cell stored without one reads as derived.
	Origin string `json:"origin,omitempty"`
	// EvidenceRefs names the cells or events this item was drawn from. Refute
	// follows them.
	EvidenceRefs []string `json:"evidenceRefs,omitempty"`
	// Supersedes lists the ids of cells this one absorbed.
	Supersedes []string `json:"supersedes,omitempty"`
	// Via is set only on a search result that was reached through a link: the id
	// of the higher-ranked result whose link surfaced it.
	Via string `json:"via,omitempty"`
	// Collapsed is set only on a search result that stands for near-duplicates
	// folded into it.
	Collapsed int `json:"collapsed,omitempty"`
}

MemoryItem is the shape of a memory cell on the server's wire: the REST API, the MCP tools and the live stream. The engine's Item is the cell's own spec; this is what a client reads, with the owner of a private item in Subject and the status spelled active, stale or superseded.

type MemoryItemSummary

type MemoryItemSummary struct {
	ID        string    `json:"id"`
	Kind      string    `json:"kind"`
	Scope     string    `json:"scope"`
	Subject   string    `json:"subject,omitempty"`
	Title     string    `json:"title"`
	Tags      []string  `json:"tags,omitempty"`
	Status    string    `json:"status"`
	UpdatedAt time.Time `json:"updatedAt"`
	HitCount  int64     `json:"hitCount"`
}

MemoryItemSummary is the light row the item lists return.

type MemoryNeighbor

type MemoryNeighbor struct {
	ID     string   `json:"id"`
	Kind   string   `json:"kind"`
	Title  string   `json:"title"`
	Status string   `json:"status"`
	Tags   []string `json:"tags,omitempty"`
	Score  float32  `json:"score"`
}

MemoryNeighbor is one nearest cell of a cell, with its similarity.

type MemoryNeighborEdge

type MemoryNeighborEdge struct {
	Source string  `json:"source"`
	Target string  `json:"target"`
	Score  float32 `json:"score"`
}

MemoryNeighborEdge links two neighbours that sit close to each other.

type MemoryNeighborhood

type MemoryNeighborhood struct {
	ID        string               `json:"id"`
	Indexed   bool                 `json:"indexed"`
	Neighbors []MemoryNeighbor     `json:"neighbors"`
	Edges     []MemoryNeighborEdge `json:"edges"`
}

MemoryNeighborhood is the nearest cells of a cell and the edges between them.

type MemoryStore

type MemoryStore struct {
	*Telemetry
	// contains filtered or unexported fields
}

MemoryStore is the server's memory: the memstore engine (items, lexical and semantic search, rerank, links, provenance), a cell index when there is an embedder, and a hub that carries changes to connected clients, behind the method set the REST API, the MCP tools and the voice tools use. Items cross this boundary as MemoryItem, the wire shape.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore creates a keyword-only MemoryStore (no embedding endpoint).

func NewMemoryStoreWithEmbedder

func NewMemoryStoreWithEmbedder(embedder *index.Embedder, logger zerolog.Logger) *MemoryStore

NewMemoryStoreWithEmbedder creates a MemoryStore with a semantic index.

func (*MemoryStore) AllItems

func (s *MemoryStore) AllItems(subject string) []*MemoryItem

AllItems returns the active shared items of a subject (empty matches all).

func (*MemoryStore) ConsolidateMemory

func (s *MemoryStore) ConsolidateMemory(subject string, apply bool) (ConsolidationReport, []*MemoryItem)

ConsolidateMemory groups the active shared cells of the dedup kinds (optionally of one subject) into near-duplicate clusters and, when apply is true, retires every member but each cluster's keeper (status superseded, the keeper's Supersedes lists them). apply=false returns the same groups and writes nothing. The second result is the changed cells, which a caller backed by the cluster persists.

func (*MemoryStore) Context

func (s *MemoryStore) Context(anchor string, maxDepth int) (anchors []*MemoryItem, activated []ScoredItem)

Context returns the cells an anchor activates: the anchor cells themselves and everything reachable over ThoughtLinks, by spreading activation. Each hop multiplies the score by 0.6 and the link's weight, a cell keeps the best score any path gave it, and at most maxDepth hops are followed (default 3, at most 8). Only shared, active cells take part.

func (*MemoryStore) Count

func (s *MemoryStore) Count() int

Count is the number of active items.

func (*MemoryStore) Delete

func (s *MemoryStore) Delete(id string) bool

Delete removes a memory item by ID. It reports whether the item was held. The vector goes too, whether or not the item was held here: a vector left behind keeps taking top-k slots. A private item's delete reaches only its owner.

func (*MemoryStore) EmbedMetrics

func (s *MemoryStore) EmbedMetrics() index.EmbedMetrics

EmbedMetrics returns the embedder's call and cache counters.

func (*MemoryStore) Expire

func (s *MemoryStore) Expire(id string) *MemoryItem

Expire retires a cell without a verdict on it: it is out of date, not wrong (Refute is for wrong). The cell is kept with status stale and leaves every index. The returned item is already stored in the process; a caller backed by the cluster persists it. nil when id names no cell.

func (*MemoryStore) ExportAll

func (s *MemoryStore) ExportAll() []*MemoryItem

ExportAll returns a copy of every item the store holds, private and retired ones included, for a program that persists the store itself.

func (*MemoryStore) FilterItems

func (s *MemoryStore) FilterItems(subject, kind string) []*MemoryItem

FilterItems returns the active items of a subject and kind (empty matches all), whatever their scope, but never the system configuration.

func (*MemoryStore) ForgetCell added in v2.3.0

func (s *MemoryStore) ForgetCell(id string)

ForgetCell drops an item whose cell was deleted, here or on another replica, from the store and the vector index.

func (*MemoryStore) Get

func (s *MemoryStore) Get(id string) *MemoryItem

Get returns a single active memory item by ID, or nil if not found.

func (*MemoryStore) GetItem

func (s *MemoryStore) GetItem(id string) *MemoryItem

GetItem returns a memory item by ID in any status, or nil if not found.

func (*MemoryStore) Hub added in v2.3.0

func (s *MemoryStore) Hub() *hub.Hub

Hub is the hub that carries memory events to connected clients.

func (*MemoryStore) KeywordSearch

func (s *MemoryStore) KeywordSearch(query, kind, subject string, limit int) []*MemoryItem

KeywordSearch scans active items by lexical match on title and content. With a subject it also covers that person's private notes.

func (*MemoryStore) KeywordSearchPerKind

func (s *MemoryStore) KeywordSearchPerKind(query, kind, subject string, limit, perKind int) []*MemoryItem

KeywordSearchPerKind is KeywordSearch with per-kind fairness: at most perKind results per kind, so a dominant kind cannot monopolise the limit and starve smaller categories. Used by the graph search, which spans all kinds over a skewed store.

func (*MemoryStore) KnowledgeIndex

func (s *MemoryStore) KnowledgeIndex(subject string) []string

KnowledgeIndex lists the titles of the shared active items of a subject.

func (*MemoryStore) List

func (s *MemoryStore) List(kind, scope, subject string, limit int) []*MemoryItem

List returns active items filtered by kind, scope and subject up to limit.

func (*MemoryStore) ListRecent

func (s *MemoryStore) ListRecent(kind, scope, subject string, limit int) []*MemoryItem

ListRecent returns active items matching kind/scope/subject sorted newest-first by CreatedAt (UpdatedAt breaks ties), capped to limit. Unlike List, the result is time-ordered: this is the recall path for episodic ("what happened last") memory, where "the latest creation time" is the selection criterion.

func (*MemoryStore) ListSummary

func (s *MemoryStore) ListSummary(kind, scope, subject string, limit int) (items []MemoryItemSummary, total int)

ListSummary is List with light rows and the total that matched.

func (*MemoryStore) ListSummaryPerCategory added in v2.4.4

func (s *MemoryStore) ListSummaryPerCategory(perCategory int, categories []CategoryConfig) (items []MemoryItemSummary, total int, totalsByKind, totalsByCategory map[string]int)

ListSummaryPerCategory is ListSummaryPerKind with the cap and the totals taken per display category (ADR-035): a category configured with a tag narrows its kind, so a kind split by tag into several categories gets perCategory rows for each of them instead of perCategory rows to share. An item's category key is "kind|tag" for the first tag of its kind's tagged categories it carries (the UI's categoryOf order), else its kind. Without tagged categories this is ListSummaryPerKind.

func (*MemoryStore) ListSummaryPerKind

func (s *MemoryStore) ListSummaryPerKind(perKind int) (items []MemoryItemSummary, total int, totalsByKind map[string]int)

ListSummaryPerKind is ListSummary over the shared items with at most perKind rows of any one kind, and the true total of each kind: the UI shows those as the real category counts, independent of the per-kind render cap.

func (*MemoryStore) MirrorCell added in v2.3.0

func (s *MemoryStore) MirrorCell(cell *mcell.MemoryCell) bool

MirrorCell reflects a cell into the store: the keyword and vector-independent state, and the change events, follow what the cluster holds. A change this process made itself is a no-op. A private cell written under the older rule, where Subject named the owner, reads as owned by that subject until the cell is migrated. It reports whether the store changed.

func (*MemoryStore) Neighborhood

func (s *MemoryStore) Neighborhood(id string, top int) *MemoryNeighborhood

Neighborhood returns the top nearest shared cells of a cell and the edges between them. Indexed is false when the cell is not in the vector index.

func (*MemoryStore) PublishHit

func (s *MemoryStore) PublishHit(itemIDs []string, kind string, kinds []string)

PublishHit broadcasts a memory search hit event. kinds must be parallel to itemIDs. The event reaches every connected client, so it carries ids and kinds only: the query is what a person asked and is not theirs to share, and callers pass shared items only (see sharedHit).

func (*MemoryStore) Refute

func (s *MemoryStore) Refute(ctx context.Context, id, reason string, evidence []string) (changed []*MemoryItem, found bool)

Refute marks a cell wrong: it records {reason, evidence, at} under Data["refuted"], retires the cell (status stale) and retires every active derived cell that cites it, one hop only: an evidence ref naming the id or a link to it. Observed, human and verified cells are never cascaded. The returned items are the changed cells, the refuted one first, already stored in the process; a caller backed by the cluster persists each. found is false when id names no cell.

func (*MemoryStore) Restore

func (s *MemoryStore) Restore(ctx context.Context, it *MemoryItem)

Restore puts an item back as it was exported: ids, origin, status, links, timestamps and deadline are kept as given. An active shared item is indexed again (keyword and, with an embedder, semantic); a retired or private one is held and left out of the shared indexes, exactly as Upsert would leave it.

func (*MemoryStore) Search

func (s *MemoryStore) Search(ctx context.Context, query, kind, subject string, limit int) []*MemoryItem

Search returns items matching the query. It runs lexical search first; when a semantic index is configured and the top lexical hit is not a confident match it fuses semantic results in by rank fusion, then reranks. With a subject, the subject's shared items are searched and so are the private notes of the person the subject names (lexically: a private note is never embedded). A query that is a cell's id goes first.

func (*MemoryStore) SetCacheReader

func (s *MemoryStore) SetCacheReader(r client.Reader, namespace string)

SetCacheReader wires the cluster reader the stats flusher reads with.

func (*MemoryStore) SetCandidateFanout

func (s *MemoryStore) SetCandidateFanout(n int)

SetCandidateFanout overrides how many times the limit each search channel pulls while source collapse will shrink the pool again. Values below 1 mean 1.

func (*MemoryStore) SetEpisodicTTL

func (s *MemoryStore) SetEpisodicTTL(ttl time.Duration)

SetEpisodicTTL sets the max age before an episodic memory is forgotten. 0 disables expiry. Call once at startup before serving.

func (*MemoryStore) SetLexicalShortCircuit

func (s *MemoryStore) SetLexicalShortCircuit(on bool)

SetLexicalShortCircuit turns the lexical short-circuit on or off. On (the default of an unconfigured store), a confident top lexical hit returns without consulting the semantic channel; off, a search always fuses the two when there is a vector index. The right call for short named-entity queries is on, and for long natural-language questions asked over long cells it is off.

func (*MemoryStore) SetLinkExpansion

func (s *MemoryStore) SetLinkExpansion(on bool)

SetLinkExpansion turns the optional one-hop ThoughtLink expansion of search results on or off (default off).

func (s *MemoryStore) SetLinks(id string, links []mcell.ThoughtLink) bool

SetLinks replaces the ThoughtLinks of an item. It is the write path for links on items that were never persisted: an ingest pipeline deriving atomic-fact cells from an episode links each fact back to its source with it. It reports false when id names no item.

func (*MemoryStore) SetRecallShaping

func (s *MemoryStore) SetRecallShaping(on bool)

SetRecallShaping turns the near-duplicate collapse and the state-aware rerank on or off (default on). Off, a search returns the fused order unchanged.

func (*MemoryStore) SetSemanticScoreGap

func (s *MemoryStore) SetSemanticScoreGap(gap float32)

SetSemanticScoreGap overrides the semantic relevance cutoff: the absolute cosine gap below the best visible hit past which semantic results are dropped. 0 disables the cutoff.

func (*MemoryStore) SetSourceCollapse

func (s *MemoryStore) SetSourceCollapse(relation string)

SetSourceCollapse turns on source collapse for links of the given relation; "" turns it off. A hit with an outbound link of that relation is replaced by the item it links to (a fact by the episode it was drawn from).

func (*MemoryStore) SetWriteDedup

func (s *MemoryStore) SetWriteDedup(on bool)

SetWriteDedup turns write-side near-duplicate merging on or off (default on): an Upsert that restates an existing stable_fact, operational_pattern, watch_item or hypothesis lands on that cell instead of minting a peer.

func (*MemoryStore) SetWriter

func (s *MemoryStore) SetWriter(c client.Client)

SetWriter wires the cluster client the stats flusher writes with.

func (*MemoryStore) StartEpisodicReaper

func (s *MemoryStore) StartEpisodicReaper(ctx context.Context, syncer *CellSyncer, every time.Duration)

StartEpisodicReaper runs a background sweep that deletes episodic memories older than episodicTTL (the "forget" mechanism). One goroutine, a ticker, exits on ctx cancel. A no-op when TTL<=0 or every<=0. syncer removes the backing cell (nil keeps the sweep in process).

func (*MemoryStore) Supersede

func (s *MemoryStore) Supersede(ctx context.Context, oldID, newID, kind, scope, title, content, subject, confidence string, tags []string, opts ...UpsertOption) (changed []*MemoryItem, errMsg string)

Supersede replaces the cell oldID with a new one: the new cell is written (as Upsert writes it, so origin, id handling and size caps apply) and lists oldID in Supersedes, and the old cell is retired with status superseded. A derived write cannot supersede a human cell. The returned items are the new cell then the old one, both already stored in the process.

func (*MemoryStore) Upsert

func (s *MemoryStore) Upsert(ctx context.Context, id, kind, scope, title, content, subject, confidence, eventDate string, data map[string]mcell.DataEntry, tags []string, opts ...UpsertOption) (UpsertResult, string)

Upsert creates or updates a memory item matched by ID (if provided) or auto-generates one. The write's provenance comes from ctx (withWriteOrigin): an identified person is human, anything else derived. A restated stable_fact, operational_pattern, watch_item or hypothesis lands on the cell it repeats (status "merged"). The owner of a private item is its subject.

type QuotaUsage

type QuotaUsage struct {
	Used      int64           `json:"used"`
	Limit     int64           `json:"limit"`
	Remaining int64           `json:"remaining"`
	Breakdown *TokenBreakdown `json:"breakdown,omitempty"`
}

QuotaUsage is the per-user daily token quota snapshot pushed to the browser. Used/Limit/Remaining are the daily cap (total tokens). Breakdown is the current session's cumulative per-modality split — what the UI shows to explain where the tokens go (audio dominates cost), since the daily store keeps only a fused total.

type ScoredItem

type ScoredItem struct {
	Item  *MemoryItem
	Score float32
}

ScoredItem is a cell reached by Context and how strongly.

type SearchMetrics

type SearchMetrics struct {
	TotalSearches     int64 `json:"totalSearches"`
	KeywordHits       int64 `json:"keywordHits"`
	KWVoiceHits       int64 `json:"kwVoiceHits"`
	KWVoiceMisses     int64 `json:"kwVoiceMisses"`
	KWTrigramHits     int64 `json:"kwTrigramHits"`
	SemanticCalls     int64 `json:"semanticCalls"`
	SemanticFallbacks int64 `json:"semanticFallbacks"`
}

SearchMetrics tracks search operation counters since process start.

type SecretStore

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

SecretStore holds Secret data watched via the controller-runtime cache. It is safe for concurrent use.

func NewSecretStore

func NewSecretStore() *SecretStore

func (*SecretStore) Delete

func (s *SecretStore) Delete(namespace, name string)

func (*SecretStore) Get

func (s *SecretStore) Get(namespace, name, key string) string

Get returns the value for a key in a Secret. Returns empty string if not found.

func (*SecretStore) SecretEventHandlerFuncs

func (s *SecretStore) SecretEventHandlerFuncs() toolscache.ResourceEventHandlerFuncs

SecretEventHandlerFuncs returns informer handlers that keep the store in sync.

func (*SecretStore) Set

func (s *SecretStore) Set(namespace, name string, data map[string][]byte)

type Server

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

Server is the X-Ray HTTP server.

func NewServer

func NewServer(logger zerolog.Logger, memory *MemoryStore, uiFS fs.FS, ghCfg GitHubOAuthConfig, azCfg AzureOAuthConfig, allowedOrigin string, maxBodyBytes int64, config *ConfigWatcher, opts ...ServerOption) *Server

func (*Server) Handler

func (s *Server) Handler() http.Handler

func (*Server) SetMCPAuth

func (s *Server) SetMCPAuth(auth *mcpAuthorizer, mcpHandler http.Handler)

SetMCPAuth mounts the MCP resource endpoint and the co-located OAuth 2.1 authorization server on the main mux. auth provides discovery + token issuance; mcpHandler is the bearer-guarded MCP JSON-RPC handler (built via NewMCPServer with the same authorizer). Either argument may be nil to skip. Must be called before the server starts accepting connections.

func (*Server) SetVoiceManager

func (s *Server) SetVoiceManager(vm *VoiceManager)

SetVoiceManager attaches a VoiceManager and registers the /v1/voice route. Must be called before the server starts accepting connections.

type ServerOption added in v2.4.0

type ServerOption func(*serverOptions)

ServerOption configures an optional part of the Server.

func WithGoogleOAuth added in v2.4.0

func WithGoogleOAuth(cfg GoogleOAuthConfig) ServerOption

WithGoogleOAuth turns on Google sign-in.

type Telemetry added in v2.3.0

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

Telemetry is what the server counts and persists about itself, apart from the memory it serves: search and voice counters, each user's daily voice-token spend, tool latencies, and the debounced writer that keeps them, the live sessions and the registered OAuth clients in the system_configuration cell.

func (*Telemetry) AddUserTokens added in v2.3.0

func (s *Telemetry) AddUserTokens(userHash string, n int64)

AddUserTokens accumulates Gemini Live token spend for an authenticated user on the current UTC day. Empty userHash (unauthenticated/dev sessions) is ignored — there is no stable key to meter. Rolls the counter over when the UTC day changes.

func (*Telemetry) FlushStats added in v2.3.0

func (s *Telemetry) FlushStats()

FlushStats marks stats dirty so the next flush tick persists them. Exported so the session store can request a flush on login/logout (wired via SetSessionPersistence). It no longer writes synchronously — see ADR-047 (debounced stats flush). The change lands on the next StartStatsFlusher tick.

func (*Telemetry) Metrics added in v2.3.0

func (s *Telemetry) Metrics() SearchMetrics

Metrics returns a snapshot of search operation counters.

func (*Telemetry) RestoreStats added in v2.3.0

func (s *Telemetry) RestoreStats(st *mcell.RuntimeStats)

RestoreStats loads persisted counters from a RuntimeStats snapshot (e.g. read by ConfigWatcher at startup). voiceActive is intentionally not restored — it is session-local and always starts at 0.

func (*Telemetry) StartStatsFlusher added in v2.3.0

func (s *Telemetry) StartStatsFlusher(ctx context.Context, every time.Duration)

StartStatsFlusher runs the single CR-status writer: every `every` it writes the current counters + sessions to the system_configuration CR, but only when something changed since the last write (markStatsDirty). This replaces the old per-request synchronous flush, so search/voice/login throughput is no longer gated on an etcd round-trip and N concurrent requests cause at most one write per tick. On ctx cancellation it does a final flush so the last state is durable across a graceful shutdown. Call once at startup after SetWriter. A zero or negative interval disables the loop (in-memory only).

func (*Telemetry) ToolMetrics added in v2.3.0

func (s *Telemetry) ToolMetrics() map[string]any

ToolMetrics returns per-tool call counts and average latency in milliseconds.

func (*Telemetry) TrackKWTrigramHit added in v2.3.0

func (s *Telemetry) TrackKWTrigramHit()

TrackKWTrigramHit records that the trigram index contributed at least one candidate to a search result.

func (*Telemetry) TrackKWVoiceHit added in v2.3.0

func (s *Telemetry) TrackKWVoiceHit()

TrackKWVoiceHit records that the keyword-first path in the voice search_memory tool returned enough results to skip semantic search.

func (*Telemetry) TrackKWVoiceMiss added in v2.3.0

func (s *Telemetry) TrackKWVoiceMiss()

TrackKWVoiceMiss records that keyword-first returned too few results and fell through to semantic search.

func (*Telemetry) TrackToolCall added in v2.3.0

func (s *Telemetry) TrackToolCall(name string, d time.Duration)

TrackToolCall records a tool invocation and its execution duration.

func (*Telemetry) TrackVoiceCall added in v2.3.0

func (s *Telemetry) TrackVoiceCall()

TrackVoiceCall records a new voice session start and marks stats dirty.

func (*Telemetry) TrackVoiceEnd added in v2.3.0

func (s *Telemetry) TrackVoiceEnd()

TrackVoiceEnd records a voice session end and marks stats dirty.

func (*Telemetry) UserTokensToday added in v2.3.0

func (s *Telemetry) UserTokensToday(userHash string) int64

UserTokensToday returns the user's token spend for the current UTC day, or 0 if the stored counter is from a previous day (not yet rolled over) or the user is unknown.

func (*Telemetry) VoiceMetrics added in v2.3.0

func (s *Telemetry) VoiceMetrics() map[string]any

VoiceMetrics returns a snapshot of voice session counters.

type TokenBreakdown

type TokenBreakdown struct {
	InputAudio  int64 `json:"inputAudio"`
	InputText   int64 `json:"inputText"`
	OutputAudio int64 `json:"outputAudio"`
	OutputText  int64 `json:"outputText"`
	Cached      int64 `json:"cached"`
}

TokenBreakdown is the per-modality token split for the current voice session.

type UpsertOption

type UpsertOption func(*upsertOpts)

UpsertOption sets an optional field of an Upsert.

func WithEvidenceRefs

func WithEvidenceRefs(refs []string) UpsertOption

WithEvidenceRefs records the cells or events the item was drawn from. Refute follows them.

type UpsertResult

type UpsertResult struct {
	ID     string      `json:"id"`
	Status string      `json:"status"` // "created", "updated" or "merged"
	Item   *MemoryItem `json:"-"`
}

UpsertResult is the outcome of an Upsert: the id, whether the item was created, updated or merged into the cell it restated, and the stored item.

type VoiceBrowserMessage

type VoiceBrowserMessage struct {
	Audio          []byte   `json:"audio,omitempty"`
	AudioStreamEnd bool     `json:"audioStreamEnd,omitempty"`
	Text           string   `json:"text,omitempty"`
	Ping           bool     `json:"ping,omitempty"`
	Pinned         []string `json:"pinned,omitempty"` // live update of pinned item IDs
}

VoiceBrowserMessage is the JSON envelope from the browser.

type VoiceManager

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

VoiceManager manages active voice sessions. Lazily initialises the genai client.

func NewVoiceManager

func NewVoiceManager(memory *MemoryStore, config *ConfigWatcher, secrets *SecretStore, syncer *CellSyncer, namespace, gcpProject, gcpLocation, geminiKey, voiceModel, voiceName string, userDailyAudioTokenLimit int64, maxOutputTokens, headroomLimit int, affectiveDialog, proactiveAudio bool, allowedOrigin string, maxConcurrent int, logger zerolog.Logger) *VoiceManager

NewVoiceManager creates a VoiceManager. syncer may be nil when k8s is unavailable. allowedOrigin scopes the WebSocket Origin check (empty = allow any, dev only). maxConcurrent caps simultaneous sessions (0 = unlimited).

func (*VoiceManager) ServeHTTP

func (m *VoiceManager) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP upgrades the request to a WebSocket and runs a voice session.

func (*VoiceManager) SetMemMaps

func (m *VoiceManager) SetMemMaps(deps *recallmcp.Deps)

SetMemMaps wires the embedded code-graph snapshot into voice sessions so the model gets search_graph et al. as NATIVE in-process tools (direct function calls — no MCP). No-op effect when deps is nil. Call before serving.

func (*VoiceManager) Shutdown

func (m *VoiceManager) Shutdown()

Shutdown closes all active sessions.

func (*VoiceManager) UserQuota

func (m *VoiceManager) UserQuota(rawUserID string) (used, limit, remaining int64, ok bool)

UserQuota reports the per-user daily token quota for the given raw SSO ID. limit is the effective cap (0 = unlimited); used is today's spend; remaining is limit-used floored at 0. ok is false when there is no cap to report — either no limit is configured or the user is unauthenticated (empty rawUserID).

func (*VoiceManager) VoiceMetrics

func (m *VoiceManager) VoiceMetrics() map[string]any

VoiceMetrics returns lifetime voice session counters.

type VoiceServerMessage

type VoiceServerMessage struct {
	Audio           []byte      `json:"audio,omitempty"`
	Transcript      string      `json:"transcript,omitempty"`
	InputTranscript string      `json:"inputTranscript,omitempty"`
	ToolCall        string      `json:"toolCall,omitempty"`
	ToolResult      string      `json:"toolResult,omitempty"`
	Interrupt       bool        `json:"interrupt,omitempty"`
	Error           string      `json:"error,omitempty"`
	Done            bool        `json:"done,omitempty"`
	Quota           *QuotaUsage `json:"quota,omitempty"` // pushed per turn while a daily limit is in effect
}

VoiceServerMessage is the JSON envelope sent to the browser.

type VoiceSession

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

VoiceSession is one active browser ↔ Gemini Live session.

func (*VoiceSession) Close

func (s *VoiceSession) Close()

Close cancels the session and waits for cleanup.

type VoiceSessionConfig

type VoiceSessionConfig struct {
	SystemPrompt      string
	Greeting          string
	VoiceName         string
	VoiceModel        string
	EnabledTools      []string
	MCPServers        []MCPServerRef
	WriteAllowedUsers []string // usernames permitted to invoke write tools via voice
	// MaxUserDailyAudioTokens caps per-user Gemini Live AUDIO token spend (audio-in
	// + audio-out) per UTC day. 0 means "not set in CR" — the caller falls back to
	// the --user-daily-audio-token-limit flag. See ADR-042.
	MaxUserDailyAudioTokens int64
	// MaxItemContentChars caps each search-result item's content (headroom
	// LossyCompress budget). 0 means "not set in CR" — the caller falls back to the
	// --headroom-content-limit flag. See ADR-038.
	MaxItemContentChars int
	// AffectiveDialog / ProactiveAudio are native-audio quality toggles. nil means
	// "not set in CR" — the caller falls back to the corresponding flag.
	AffectiveDialog *bool
	ProactiveAudio  *bool
	// Categories is the UI display category taxonomy (ADR-035 item 4). Empty
	// means the UI falls back to its in-repo default taxonomy.
	Categories []CategoryConfig
}

VoiceSessionConfig is the parsed, typed voice session configuration loaded from the system_configuration MemoryCell CR.

Jump to

Keyboard shortcuts

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