Documentation
¶
Overview ¶
Package xray is the Go SDK for X-Ray, an agentic memory substrate for local and Kubernetes-native use.
A Client talks to a running X-Ray over MCP (streamable HTTP). It covers both kinds of memory the server holds:
- Authored memory: typed items that agents and people write. Search, List, Fetch, Upsert, Delete, Refute, Expire, Supersede, Consolidate and Context.
- Derived memory: a graph of code, documentation and manifests. SearchGraph, SearchGraphDocs, Node, Impact, Risk, GraphStats and UpdateGraph, and the agent notes Remember, Recall and Forget.
Connect to the memory server (default port 8083, or /mcp on the main port when OAuth is on) or to a graph server (xray graph serve, port 8084, or serve-snapshots, port 8085):
c, err := xray.Connect(ctx, "http://localhost:8083/mcp", xray.WithToken(token))
if err != nil { ... }
defer c.Close()
res, err := c.Upsert(ctx, xray.Write{
Kind: xray.KindStableFact, Scope: xray.ScopePublic, Confidence: "high",
Title: "Checkout retries on 503", Content: "Three times, with jitter.",
})
items, err := c.Search(ctx, "payment retries", xray.WithLimit(5))
A server answers only the tools it has: the memory tools on the memory server, the graph tools on a graph server, and both on a memory server that loads maps. Call reports the rest as an error.
Which credential the server sees decides how a write is classed. A write made with an OAuth token that names a person is recorded as human; one made with the shared secret is recorded as derived. The SDK cannot set it.
Example ¶
Write an item to X-Ray and search for it by meaning.
package main
import (
"context"
"fmt"
"log"
"github.com/triageagent-dev/x-ray/v2/pkg/xray"
)
func main() {
ctx := context.Background()
c, err := xray.Connect(ctx, "http://localhost:8083/mcp", xray.WithToken("shared-secret"))
if err != nil {
log.Fatal(err)
}
defer c.Close()
if _, err := c.Upsert(ctx, xray.Write{
Kind: xray.KindStableFact, Scope: xray.ScopePublic, Confidence: "high",
Title: "Checkout retries on 503",
Content: "The payment client retries a 503 three times with jitter.",
Subject: "checkout",
}); err != nil {
log.Fatal(err)
}
items, err := c.Search(ctx, "how does payment retry?", xray.WithLimit(5))
if err != nil {
log.Fatal(err)
}
for _, it := range items {
fmt.Println(it.Title, it.Origin)
}
}
Output:
Index ¶
- Constants
- func IsNotFound(err error) bool
- type Client
- func (c *Client) Call(ctx context.Context, tool string, args map[string]any) (json.RawMessage, error)
- func (c *Client) Close() error
- func (c *Client) Consolidate(ctx context.Context, apply bool, opts ...QueryOption) (*ConsolidationReport, error)
- func (c *Client) Context(ctx context.Context, anchor string, maxDepth int) ([]ContextItem, error)
- func (c *Client) Delete(ctx context.Context, id string, opts ...QueryOption) error
- func (c *Client) Expire(ctx context.Context, id string, opts ...QueryOption) error
- func (c *Client) Fetch(ctx context.Context, ids []string, opts ...QueryOption) ([]Item, error)
- func (c *Client) Forget(ctx context.Context, qn string) (*Forgotten, error)
- func (c *Client) Get(ctx context.Context, id string, opts ...QueryOption) (*Item, error)
- func (c *Client) GraphStats(ctx context.Context, opts ...GraphOption) (json.RawMessage, error)
- func (c *Client) Impact(ctx context.Context, seeds []string, depth, maxNodes int) (*Impact, error)
- func (c *Client) List(ctx context.Context, opts ...QueryOption) ([]Item, error)
- func (c *Client) Node(ctx context.Context, qn string) (*Node, error)
- func (c *Client) Recall(ctx context.Context, query string, limit int) (*Recalled, error)
- func (c *Client) Recent(ctx context.Context, opts ...QueryOption) ([]Episode, error)
- func (c *Client) Refute(ctx context.Context, id, reason string, evidence []string, opts ...QueryOption) (*RefuteResult, error)
- func (c *Client) Remember(ctx context.Context, text string, opts ...RememberOption) (*Remembered, error)
- func (c *Client) Risk(ctx context.Context, qn string, depth, maxNodes int) (*Risk, error)
- func (c *Client) Search(ctx context.Context, query string, opts ...QueryOption) ([]Item, error)
- func (c *Client) SearchGraph(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
- func (c *Client) SearchGraphDocs(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
- func (c *Client) Skill(ctx context.Context, name string) (json.RawMessage, error)
- func (c *Client) Skills(ctx context.Context) (json.RawMessage, error)
- func (c *Client) Summary(ctx context.Context) (*Summary, error)
- func (c *Client) Supersede(ctx context.Context, oldID string, w Write) (*SupersedeResult, error)
- func (c *Client) Tools(ctx context.Context) ([]string, error)
- func (c *Client) UpdateGraph(ctx context.Context, paths ...string) (*UpdateResult, error)
- func (c *Client) Upsert(ctx context.Context, w Write) (*WriteResult, error)
- type CodeGraph
- type ConsolidationGroup
- type ConsolidationReport
- type ContextItem
- type Episode
- type Forgotten
- type GraphOption
- type GraphQuery
- type Hit
- type Impact
- type ImpactNode
- type Item
- type Link
- type Memory
- type Node
- type Note
- type Option
- type Query
- type QueryOption
- type Recalled
- type RefuteResult
- type RememberOption
- type Remembered
- type Risk
- type SearchResult
- type Summary
- type SupersedeResult
- type ToolError
- type UpdateResult
- type Write
- type WriteResult
Examples ¶
Constants ¶
const ( KindStableFact = "stable_fact" KindOperationalPattern = "operational_pattern" KindActiveConcern = "active_concern" KindWatchItem = "watch_item" KindHypothesis = "hypothesis" KindRecentChange = "recent_change" KindEvent = "event" KindNote = "note" KindSystemIdentity = "system_identity" KindUnknown = "unknown" KindEpisodic = "episodic" KindMentalMap = "mental_map" )
Memory kinds an agent may write (Write.Kind). Search and List also accept KindEpisodic and KindMentalMap as filters.
const ( ScopePublic = "public" ScopePrivate = "private" )
Scopes. A private item is visible only to callers that pass its subject.
const ( OriginObserved = "observed" OriginDerived = "derived" OriginHuman = "human" OriginVerified = "verified" )
Origins, as read back on an Item. An item stored without one reads as derived.
const Version = "2.0.0"
Version is the SDK's own version, sent to the server as the client version.
Variables ¶
This section is empty.
Functions ¶
func IsNotFound ¶
IsNotFound reports whether err is a ToolError for an item that does not exist or that the caller may not see.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a connection to an X-Ray MCP endpoint. It is safe for concurrent use.
func Connect ¶
Connect opens a session with the MCP endpoint at url, for example http://localhost:8083/mcp. The caller closes the Client.
func (*Client) Call ¶
func (c *Client) Call(ctx context.Context, tool string, args map[string]any) (json.RawMessage, error)
Call invokes any tool by name and returns its JSON reply. The typed methods are built on it; use it for a tool added after this SDK was written.
func (*Client) Consolidate ¶
func (c *Client) Consolidate(ctx context.Context, apply bool, opts ...QueryOption) (*ConsolidationReport, error)
Consolidate reports near-duplicate facts. With apply it retires the restatements. WithSubject restricts the sweep to one subject.
func (*Client) Context ¶
Context follows links from an anchor (an id, a subject or part of a title) with decaying activation, and returns the anchor items and the neighbourhood they reach, by relevance. maxDepth 0 uses the server default (3, at most 8).
func (*Client) Fetch ¶
Fetch returns the items with the given ids, in that order. A missing id, or a private item the subject does not own, is left out.
func (*Client) Forget ¶
Forget removes one note by the qualified name Remember returned. The server must run with -writable.
func (*Client) GraphStats ¶
func (c *Client) GraphStats(ctx context.Context, opts ...GraphOption) (json.RawMessage, error)
GraphStats returns node and edge counts, languages and the router summary of a map, as the server reports them.
func (*Client) Impact ¶
Impact returns what a change to the seed symbols reaches over calls and imports. depth and maxNodes of 0 use the server defaults (2 and 200).
Example ¶
Ask a graph server what a change reaches.
package main
import (
"context"
"fmt"
"log"
"github.com/triageagent-dev/x-ray/v2/pkg/xray"
)
func main() {
ctx := context.Background()
c, err := xray.Connect(ctx, "http://127.0.0.1:8084/mcp")
if err != nil {
log.Fatal(err)
}
defer c.Close()
hits, err := c.SearchGraph(ctx, "where are snapshots encrypted", xray.OnMap("xray"), xray.Limit(3))
if err != nil {
log.Fatal(err)
}
if len(hits.Results) == 0 {
return
}
imp, err := c.Impact(ctx, []string{hits.Results[0].QN}, 2, 50)
if err != nil {
log.Fatal(err)
}
for _, n := range imp.Impacted {
fmt.Println(n.Depth, n.QN)
}
}
Output:
func (*Client) Recall ¶
Recall reads notes back: newest first, or ranked by query when one is given.
func (*Client) Refute ¶
func (c *Client) Refute(ctx context.Context, id, reason string, evidence []string, opts ...QueryOption) (*RefuteResult, error)
Refute retires an item that is wrong, records the reason and evidence on it, and retires every active derived item that cites it.
Example ¶
Refute an item that turned out to be wrong, and everything derived from it.
package main
import (
"context"
"fmt"
"log"
"github.com/triageagent-dev/x-ray/v2/pkg/xray"
)
func main() {
ctx := context.Background()
c, err := xray.Connect(ctx, "http://localhost:8083/mcp", xray.WithToken("shared-secret"))
if err != nil {
log.Fatal(err)
}
defer c.Close()
out, err := c.Refute(ctx, "item-id", "the spikes predate the cache", []string{"event-1"})
if err != nil {
log.Fatal(err)
}
fmt.Println(out.Status, out.Cascaded)
}
Output:
func (*Client) Remember ¶
func (c *Client) Remember(ctx context.Context, text string, opts ...RememberOption) (*Remembered, error)
Remember writes a short note into the server's notes map, so a later session can read it back. The server must run with -writable.
func (*Client) Search ¶
Search ranks items by relevance to query, by keyword and by meaning. Without WithSubject only shared items are searched.
func (*Client) SearchGraph ¶
func (c *Client) SearchGraph(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
SearchGraph finds code and manifests by meaning.
func (*Client) SearchGraphDocs ¶
func (c *Client) SearchGraphDocs(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
SearchGraphDocs finds documentation prose by meaning: ADRs, READMEs, todos.
func (*Client) Supersede ¶
Supersede writes w as the replacement of oldID and retires the old item. w.ID names the replacement, or is left empty to have one generated.
func (*Client) UpdateGraph ¶
UpdateGraph re-indexes changed files in the running server. The server refuses it when its snapshot directory is read-only.
type CodeGraph ¶
type CodeGraph interface {
SearchGraph(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
SearchGraphDocs(ctx context.Context, query string, opts ...GraphOption) (*SearchResult, error)
Node(ctx context.Context, qn string) (*Node, error)
Impact(ctx context.Context, seeds []string, depth, maxNodes int) (*Impact, error)
Risk(ctx context.Context, qn string, depth, maxNodes int) (*Risk, error)
GraphStats(ctx context.Context, opts ...GraphOption) (json.RawMessage, error)
UpdateGraph(ctx context.Context, paths ...string) (*UpdateResult, error)
}
CodeGraph is the code-graph API. *Client implements it against a running graph server, and codegraph.Graph implements it in the caller's own process.
type ConsolidationGroup ¶
type ConsolidationGroup struct {
Kind string `json:"kind"`
Keep string `json:"keep"`
KeepTitle string `json:"keep_title"`
Superseded []string `json:"superseded"`
Scores []float64 `json:"scores"`
}
ConsolidationGroup is a set of near-duplicates: the item to keep and the restatements it absorbs.
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 Consolidate.
type ContextItem ¶
type ContextItem struct {
ID string `json:"id"`
Kind string `json:"kind"`
Subject string `json:"subject,omitempty"`
Title string `json:"title"`
Content string `json:"content"`
Confidence string `json:"confidence,omitempty"`
IsAnchor bool `json:"is_anchor,omitempty"`
Score float32 `json:"score,omitempty"`
}
ContextItem is an item reached from an anchor.
type Episode ¶
type Episode struct {
ID string `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
CreatedAt string `json:"created_at"`
}
Episode is a past-session memory, as Recent returns it.
type GraphOption ¶
type GraphOption func(*graphOptions)
GraphOption narrows a graph call.
func Attr ¶
func Attr(kv string) GraphOption
Attr keeps nodes whose attribute matches, written "key=value".
func Limit ¶
func Limit(n int) GraphOption
Limit caps the number of hits (default 5, at most 20 on the server).
func OfKind ¶
func OfKind(kind string) GraphOption
OfKind keeps only nodes of one kind, for example "Func", "Class" or "Doc".
func OnMap ¶
func OnMap(name string) GraphOption
OnMap scopes a call to one map. Without it a search is routed by meaning to the best-matching map.
func OrderBy ¶
func OrderBy(order string) GraphOption
OrderBy sorts by an attribute, written "key" or "key:desc".
type GraphQuery ¶
GraphQuery is the resolved form of a list of GraphOption.
func ResolveGraph ¶
func ResolveGraph(opts ...GraphOption) GraphQuery
ResolveGraph applies opts and returns what they asked for.
type Hit ¶
type Hit struct {
// QN is the qualified name, repo//path::Symbol. Pass it to Node or Impact.
QN string `json:"qn"`
Kind string `json:"kind"`
Title string `json:"title"`
File string `json:"file"`
Line int `json:"line"`
Score float64 `json:"score"`
Preview string `json:"preview"`
Repo string `json:"repo,omitempty"`
// Parent is the containing node of a child hit.
Parent string `json:"parent,omitempty"`
}
Hit is one graph search result: a lean pointer to a node.
type Impact ¶
type Impact struct {
Seeds []string `json:"seeds"`
Depth int `json:"depth"`
Count int `json:"count"`
Impacted []ImpactNode `json:"impacted"`
}
Impact is the blast radius of changing some symbols.
type ImpactNode ¶
ImpactNode is a node a change reaches, and how many hops away it is.
type Item ¶
type Item 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 string `json:"eventDate,omitempty"`
StaleAfter *time.Time `json:"staleAfter,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Links []Link `json:"links,omitempty"`
// Origin is who wrote the item: observed, derived, human or verified.
Origin string `json:"origin,omitempty"`
// EvidenceRefs names the items or events this one was drawn from.
EvidenceRefs []string `json:"evidenceRefs,omitempty"`
// Supersedes lists the items this one absorbed.
Supersedes []string `json:"supersedes,omitempty"`
// Via is set on a search result that was added by following a link from the
// result of that id.
Via string `json:"via,omitempty"`
// Collapsed counts the near-duplicates folded into a search result.
Collapsed int `json:"collapsed,omitempty"`
}
Item is one memory item.
type Link ¶
type Link struct {
ToID string `json:"toId"`
Relation string `json:"relation"`
Weight float32 `json:"weight"`
EvidenceRefs []string `json:"evidenceRefs,omitempty"`
}
Link is a typed, weighted edge to another item.
type Memory ¶
type Memory interface {
Search(ctx context.Context, query string, opts ...QueryOption) ([]Item, error)
List(ctx context.Context, opts ...QueryOption) ([]Item, error)
Fetch(ctx context.Context, ids []string, opts ...QueryOption) ([]Item, error)
Get(ctx context.Context, id string, opts ...QueryOption) (*Item, error)
Summary(ctx context.Context) (*Summary, error)
Recent(ctx context.Context, opts ...QueryOption) ([]Episode, error)
Upsert(ctx context.Context, w Write) (*WriteResult, error)
Delete(ctx context.Context, id string, opts ...QueryOption) error
Refute(ctx context.Context, id, reason string, evidence []string, opts ...QueryOption) (*RefuteResult, error)
Expire(ctx context.Context, id string, opts ...QueryOption) error
Supersede(ctx context.Context, oldID string, w Write) (*SupersedeResult, error)
Consolidate(ctx context.Context, apply bool, opts ...QueryOption) (*ConsolidationReport, error)
Context(ctx context.Context, anchor string, maxDepth int) ([]ContextItem, error)
}
Memory is the authored-memory API. *Client implements it against a running X-Ray, and memory.Store implements it in the caller's own process, so an agent written against Memory runs on either.
type Node ¶
type Node struct {
QN string `json:"qn"`
Repo string `json:"repo"`
Kind string `json:"kind"`
Title string `json:"title"`
File string `json:"file"`
LineStart int `json:"line_start"`
LineEnd int `json:"line_end"`
Language string `json:"language"`
Content string `json:"content"`
Attrs map[string]string `json:"attrs,omitempty"`
// Calls, CalledBy and Imports are qualified names, in both directions.
Calls []string `json:"calls,omitempty"`
CalledBy []string `json:"called_by,omitempty"`
Imports []string `json:"imports,omitempty"`
}
Node is the full body of one graph node and its relations.
type Note ¶
type Note struct {
QN string `json:"qn"`
At string `json:"at"`
Text string `json:"text"`
Title string `json:"title,omitempty"`
Expires string `json:"expires,omitempty"`
Attrs map[string]string `json:"attrs,omitempty"`
}
Note is an agent note, as Recall returns it.
type Option ¶
type Option func(*config)
Option configures Connect.
func WithClientInfo ¶
WithClientInfo names the client to the server, for its logs.
func WithHTTPClient ¶
WithHTTPClient uses hc for every request, for a proxy, custom TLS roots or a test transport. Its Timeout still applies.
func WithHeader ¶
WithHeader sends an extra header on every request.
func WithTimeout ¶
WithTimeout bounds each HTTP request. The default is 30 seconds.
type Query ¶
Query is the resolved form of a list of QueryOption, for an implementation of Memory that answers in process.
func ResolveQuery ¶
func ResolveQuery(opts ...QueryOption) Query
ResolveQuery applies opts and returns what they asked for.
type QueryOption ¶
type QueryOption func(*queryOptions)
QueryOption narrows a memory call.
func WithLimit ¶
func WithLimit(n int) QueryOption
WithLimit caps the number of results. The server applies its own ceiling.
func WithSubject ¶
func WithSubject(subject string) QueryOption
WithSubject scopes a call to one subject. It is required to reach a private item, and it is how a caller names itself.
type Recalled ¶
type Recalled struct {
Map string `json:"map"`
Count int `json:"count"`
Notes []Note `json:"notes"`
// Note says why the reply is empty or how it is ordered. Notes are what
// people said, not the record of a fact.
Note string `json:"note"`
}
Recalled is the reply to Recall.
type RefuteResult ¶
type RefuteResult struct {
ID string `json:"id"`
Status string `json:"status"`
// Cascaded lists the model-written items retired because they cited the
// refuted one.
Cascaded []string `json:"cascaded"`
}
RefuteResult is the outcome of Refute.
type RememberOption ¶
type RememberOption func(*rememberOptions)
RememberOption sets optional fields of a note.
func NoteAttrs ¶
func NoteAttrs(attrs map[string]string) RememberOption
NoteAttrs adds facets, for example about=<name>, that Recall can find it by.
func NoteTTL ¶
func NoteTTL(ttl string) RememberOption
NoteTTL sets the lifetime: a duration such as 7d, 24h or 90m, or "never".
func NoteTitle ¶
func NoteTitle(title string) RememberOption
NoteTitle names the note; it becomes part of its qualified name.
type Remembered ¶
type Remembered struct {
Map string `json:"map"`
QN string `json:"qn"`
At string `json:"at"`
Notes int `json:"notes"`
}
Remembered is the outcome of Remember.
type Risk ¶
type Risk struct {
QN string `json:"QN"`
// Level is LOW, MEDIUM or HIGH.
Level string `json:"Level"`
DirectCallers int `json:"DirectCallers"`
ModuleSpread int `json:"ModuleSpread"`
ImpactedCount int `json:"ImpactedCount"`
Modules []string `json:"Modules"`
}
Risk is a coarse change-risk signal for one symbol.
type SearchResult ¶
type SearchResult struct {
Query string `json:"query"`
Count int `json:"count"`
Results []Hit `json:"results"`
// Note explains an empty or unusual result, such as an unknown attribute.
Note string `json:"note,omitempty"`
}
SearchResult is the reply to SearchGraph and SearchGraphDocs.
type SupersedeResult ¶
type SupersedeResult struct {
ID string `json:"id"`
Superseded string `json:"superseded"`
Status string `json:"status"`
}
SupersedeResult is the outcome of Supersede.
type ToolError ¶
ToolError is a failure the server reported for a call: a refused write, a missing item, an unknown kind. Transport failures are returned as ordinary errors.
type UpdateResult ¶
type UpdateResult struct {
Changed int `json:"changed"`
Removed int `json:"removed"`
Skipped int `json:"skipped,omitempty"`
// Unowned lists the paths that belong to no served repository; they are
// never read.
Unowned []string `json:"unowned,omitempty"`
}
UpdateResult is the outcome of UpdateGraph.
type Write ¶
type Write struct {
// ID updates the item with that id; empty creates a new one.
ID string
// Kind is required. A stable_fact needs Confidence "high".
Kind string
// Scope is required: ScopePublic or ScopePrivate. A private item needs Subject.
Scope string
Title string
Content string
// Subject is the author or owner.
Subject string
Confidence string
// EventDate is RFC3339 or YYYY-MM-DD, for a kind=event item.
EventDate string
Tags []string
// EvidenceRefs names the items or events this one was drawn from, so that
// Refute can retire it with them.
EvidenceRefs []string
// Image is an optional thumbnail, ImageMIME its type (default image/jpeg).
Image []byte
ImageMIME string
}
Write is an item to create or update.
type WriteResult ¶
type WriteResult struct {
ID string `json:"id"`
Kind string `json:"kind"`
// Status is "created", "updated" or "merged". Merged means the write restated
// an item that already existed and landed on it: ID is that item's id.
Status string `json:"status"`
Title string `json:"title"`
MergedInto string `json:"merged_into,omitempty"`
}
WriteResult is the outcome of Upsert.