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

steward

package module
v0.54.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 22 Imported by: 0

README

steward

A product-agnostic platform for building AI ops & support agents over an open-source project. The engine knows nothing about any specific product — a "domain" is supplied entirely by a domain.toml. A worked example domain ships under examples/; the same engine serves any storage/infra project.

Named opspilot until v0.51.0, opsdoctor until v0.50.0 and oss-agent until v0.38.0. OPSPILOT_*, OPSDOCTOR_* and OSS_* environment variables are still read, in that order, when the STEWARD_* name is unset.

What it does

  • Builds a knowledge graph of a project's source, docs, and internal know-how by importing Understand-Anything knowledge-graph.json (AST + LLM ontology) and/or semantically ingesting prose docs/skills — all stored in cortexdb (vectors + graph).
  • Answers & diagnoses via an agent-go Agent (ReAct loop): it calls read-only probes and a GraphRAG knowledge_search tool, grounds answers in the real code/docs, and is guarded by a deterministic red-line wall that blocks destructive one-liners.
  • Triages logs (analyze-log): any file/dir/archive → de-duplicated, ranked findings (generic severities + the domain's error_patterns) → optional AI root-cause grounded in the knowledge graph.

Architecture

domain.toml ──┐                         ┌── probes (read-only diagnostics)
              ├── engine (product-free) ─┼── knowledge_search (GraphRAG / cortexdb)
repos/docs ───┘                         └── red-line safety wall (deterministic)
                          │
        agent-go Agent (ReAct) ── LLM (OpenAI-compatible) + embedder (e.g. ollama)
  • internal/domain — loads domain.toml (persona, entity/relation types, error_patterns, probes, repos, red_lines). The only place a "product" enters.
  • internal/knowledge — cortexdb wrapper (embed, semantic search, graph upsert).
  • internal/graphimport — imports Understand-Anything knowledge-graph.json.
  • internal/ingest — semantic text ingest for prose docs/skills.
  • internal/cligraph — a command line's --help pages (cobra, clap, argparse) as a graph: cli_command / cli_flag / cli_argument nodes, has_subcommand / has_flag / has_argument edges, and implemented_by from a command to its handler in the code graph.
  • internal/apigraph — an OpenAPI/Swagger spec as a graph: api_operation / api_schema nodes, accepts / returns / references edges, implemented_by to the server function and calls_api from the command that calls it.
  • internal/loganalyze — product-agnostic log triage engine.
  • internal/agents — wires the agent-go Agent (tools + lint). Plain function-calling, so a question gets one clean answer.
  • internal/safety — the red-line filter (regex, severity → unlock-key gate).
  • internal/extract — ingest-time LLM ontology extractor (raw LLM, no agent loop).

Commands

steward ask <question>       one-shot Q&A (ReAct: probes + knowledge_search)
steward diagnose <symptom>   same loop, framed for troubleshooting
steward chat                 multi-turn (history kept across turns)
steward serve                start the HTTP API (STEWARD_HTTP_ADDR, default :7634)
steward analyze-log <path>   triage a log file / dir / .tar.gz / .zip, + AI diagnosis
steward ingest <dir>         ingest *.md docs
steward ingest-cli [--repo R] <bin>   walk every subcommand's --help into a command reference AND a
                             graph (command → flag / argument / subcommand); with --repo, each
                             command is linked to the function in R's code graph that implements it
                             (--code-path <prefix>[,…] when that code is not named for the binary)
steward purge <id> | --prefix <p>  remove a source (its chunks and graph nodes) from the knowledge base
steward ingest-api [--repo R [--code-path D]] [--cli BIN] <spec>   an OpenAPI 3 / Swagger 2 spec as a
                             graph: each operation (method, path, parameters, body and response
                             fields) and the schemas it takes and returns; --repo links each to the
                             server function of its name (--code-path prefixes, tried narrowest first),
                             --cli to the command spelled with the same words
steward ingest-repo <url>    clone → understand → import (graph), or text fallback
steward refresh <url|dir>    purge one source (catches deletions) and re-import it
steward import-graph [--repo R] <f>   import an Understand-Anything knowledge-graph.json
steward search <query>       query the knowledge base directly (no LLM)
steward search-graph <query> search + one-hop graph expansion (calls/contains/…)
steward check <command...>   test a command against the red-line wall
steward domain               print the loaded domain config

HTTP API (steward serve)

A thin JSON layer over the same agent + knowledge store. Address via STEWARD_HTTP_ADDR (default :7634). Without an LLM key it serves the search endpoints only.

GET  /healthz                     status + loaded domain
POST /ask            {question}                  → {answer}        (one-shot)
POST /chat           {session_id?, message}      → {session_id, answer}  (multi-turn; history persisted)
POST /ask/stream     {question} or ?q=           → SSE token stream (falls back to one chunk)
POST /diagnose       {question}                  → {answer}        (troubleshooting framing)
POST /search         {query, top_k}              → {hits}
POST /search-graph   {query, top_k}              → {hits, related_via_graph}
POST /analyze-log    multipart 'log' file OR {path}   → triage groups; ?diagnose=true adds AI root-cause

Multi-turn history is keyed by session_id and persisted by agent-go's session store (at STEWARD_DB_PATH), so conversations survive across requests and restarts.

Two memory layers on /chat:

  • within-session — agent-go session history (same session_id).
  • cross-session — every turn is also embedded into cortexdb's memory (global conversations bucket); each new question semantically recalls relevant turns from any past conversation and prepends them as optional context. The response's recalled field reports how many were pulled. Toggle with STEWARD_CONV_MEMORY=off.

Configuration (env)

STEWARD_DOMAIN_FILE     path to the active domain.toml (e.g. examples/example/domain.toml)
STEWARD_LLM_API_KEY     LLM key (OpenAI-compatible)   STEWARD_LLM_BASE_URL / STEWARD_LLM_MODEL
STEWARD_LLM_REASONING_EFFORT  reasoning_effort sent on every chat call ("none", "low", …); a local
                        reasoning model (qwen3.x on ollama) thinks before every call without it
STEWARD_EMB_API_KEY     embedder key                  STEWARD_EMB_BASE_URL / STEWARD_EMB_MODEL / STEWARD_EMB_DIM
STEWARD_KNOWLEDGE_DB_PATH  cortexdb path (default ./data/knowledge.db)
STEWARD_HTTP_ADDR       HTTP API listen address for `serve` (default :7634)
STEWARD_CONV_MEMORY     cross-session chat memory on /chat (default on; set "off" to disable)
STEWARD_UNDERSTAND_CMD  command run in a repo to produce knowledge-graph.json
STEWARD_ALCHEMY_ADDR    alchemy gRPC address; prose is then extracted through alchemy (see below)
STEWARD_ALCHEMY_TOKEN   its bearer token   STEWARD_ALCHEMY_TLS=1 to use TLS

Any OpenAI-compatible endpoint works, and the LLM and the embedder need not be the same provider. What we run:

STEWARD_LLM_BASE_URL=https://cpa.superleo.app/v1  STEWARD_LLM_MODEL=gemini-3.8-flash-high

The embedder used to query must match the one used to build the store (e.g. ollama embeddinggemma, 768-dim) — so the LLM above can be swapped for another at any time, and the embedder cannot.

Building a new domain

  1. Write a domain.toml (see examples/example/domain.toml): persona, entity/relation types, error_patterns, probes, repos, red_lines.
  2. Point STEWARD_DOMAIN_FILE at it.
Extraction through alchemy

By default prose is read into the graph by a built-in extractor: one LLM call per chunk under the domain's vocabulary. With STEWARD_ALCHEMY_ADDR set, it is read by alchemy instead. Each document becomes one alchemy job of its chunks, checked against the vocabulary translated from domain.toml, and what comes back carries what the built-in extractor cannot say:

  • every node and edge records the job, producer, model and ontology version it came from (alchemy_* metadata), and whether a model inferred it or a source stated it;
  • a type the vocabulary does not declare, two names alchemy could not decide were one thing, and a chunk it could not read are reported per document rather than swallowed;
  • a document whose sources contradict each other is held: its vectors are stored, its graph is not written, and the log names the job and the conflict to rule on.

Entities keep the store's identity — the same gateway named in forty documents is one node — and refresh, resolve, doctor and citations work as before. steward doctor reports whether alchemy is reachable with the token given.

What alchemy holds a job on is decided in domain.toml, not by alchemy. The hold above fires on three per-relation flags, and the default for each is silence:

[[relation]]
name = "ACTIVE_ON"          # a resource is Primary on one node at a time
from = "ResourceGroup"
to   = "Node"
at_most_one_out = true      # a second such edge is a question for a person

[[relation]]
name = "replicates_to"      # both directions are true at once
from = ["Node", "Replica"]
to   = ["Node", "Replica"]
both_ways = true            # without this, alchemy reads the pair as a conflict

Both defaults are wrong for some relation in every real vocabulary. Undeclared, a symmetric relation makes alchemy read the two directions as two sources contradicting each other and withhold the whole document's graph — measured, not hypothetical: a two-line document saying node-b and node-e replicate to each other is held on both_ways's absence and stores no edges. And without at_most_one_in / at_most_one_out nothing can be a contradiction at all: a document that says a resource is on node-b and then corrects itself to node-e stores both edges and reports no disagreement, because two edges of one type between different pairs are two facts unless the vocabulary says otherwise.

So a domain that declares no ends gets alchemy's provenance and none of its refusals. examples/example/domain.toml shows both flags in place. 3. Ingest the project's repos (ingest-repo) and docs (ingest-repo text path). 4. ask / analyze-log away. No engine code changes.

Use as a library

The CLI and HTTP server are one app built on the public package github.com/liliang-cn/steward — embed the same engine in your own program:

import steward "github.com/liliang-cn/steward"

// Zero-value fields fall back to STEWARD_* env vars, then defaults.
a, err := steward.New(steward.Config{DomainFile: "domain.toml"})
if err != nil { log.Fatal(err) }
defer a.Close()

answer, _ := a.Ask(ctx, "How do I recover a StandAlone resource?")

// Stream tool calls + the grounded, cited answer:
a.Stream(ctx, question, func(e steward.Event) {
    switch e.Kind {
    case steward.EventToolCall: log.Printf("tool %s %v", e.Tool, e.Args)
    case steward.EventText:     fmt.Print(e.Text)
    }
})
method what it does
New(Config) / Close() build / release the agent (LLM + knowledge store)
Ask / Diagnose one-shot grounded answer (ReAct: probes + knowledge_search + red-line wall)
Chat(sessionID, msg) multi-turn with persisted per-session history
Stream(q, on) live tool calls + answer deltas; returns the full answer + cited sources
Search(q, k) top reranked knowledge chunks (no LLM)
CheckCommand(cmd) run a command through the deterministic red-line wall
AnalyzeLog(path) triage a log file / dir / archive into ranked problems
IngestDoc / IngestDir add docs to the knowledge base (embedded with the configured embedder)
Refresh(dir) / PurgeSource re-ingest a source (catches updates + deletions) / remove one
MCPStatus() per-server outcome of mounting Config.MCPServers (read-only tools into ReAct)

Updates re-embed with the Agent's embedder, so it must match the target DB's model/dimension (e.g. the extracted drbd-reactor.db is text-embedding-v4 / 1024-dim) — mismatched vectors corrupt retrieval.

Config is env-first, so a process configured via STEWARD_* can call steward.New(steward.Config{}). A runnable example lives in examples/lib/.

License

MIT, see LICENSE.

Documentation

Overview

Package steward is the library API for building a product-agnostic AI ops & support agent over an open-source project's code, docs, and runbooks.

The engine knows nothing about any specific product: a "domain" (persona, ontology vocabulary, log error patterns, read-only probes, destructive-command red-lines, repos) is supplied as a domain.toml or in code. The same agent then answers and diagnoses questions grounded in a GraphRAG knowledge base, streams its tool use, cites sources, triages logs, and blocks destructive commands.

Minimal use:

a, err := steward.New(steward.Config{DomainFile: "domain.toml"})
if err != nil { log.Fatal(err) }
defer a.Close()
answer, err := a.Ask(ctx, "How do I recover a resource stuck in a degraded state?")

Any Config field left zero falls back to the matching STEWARD_* environment variable and then a built-in default, so a process configured purely through the environment can call steward.New(steward.Config{}).

Index

Constants

View Source
const (
	WalkOut  = knowledge.WalkOut
	WalkIn   = knowledge.WalkIn
	WalkBoth = knowledge.WalkBoth
)

Walk directions, re-exported so a caller never imports an internal package.

View Source
const DefaultApprovalTimeout = 10 * time.Minute

DefaultApprovalTimeout is how long a held call waits when Config leaves ApprovalTimeout zero.

Variables

View Source
var ErrNoSuchApproval = errors.New("no pending approval with that id")

ErrNoSuchApproval is returned by Decide for an id that is not waiting: it was never issued, was already decided, or its turn ended.

Functions

This section is empty.

Types

type Agent

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

Agent is a configured ops/support agent. It is safe for sequential use; run one Ask/Chat/Stream at a time (the underlying ReAct loop is single-flight). Always Close it.

func New

func New(cfg Config) (*Agent, error)

New builds an Agent from cfg (env/defaults fill any zero fields).

func (*Agent) AnalyzeLog

func (a *Agent) AnalyzeLog(path string) (*LogReport, error)

AnalyzeLog triages a log file, directory, or archive (.tar.gz/.zip/…) into a ranked set of distinct problems using the domain's error patterns.

func (*Agent) Ask

func (a *Agent) Ask(ctx context.Context, question string) (string, error)

Ask runs one ReAct turn (probes + knowledge_search + red-line wall) and returns the grounded answer.

func (*Agent) Chat

func (a *Agent) Chat(ctx context.Context, sessionID, message string) (string, error)

Chat runs a turn within a persistent session: history keyed by sessionID is loaded and saved by the agent, so follow-ups remember earlier turns.

func (*Agent) CheckCommand

func (a *Agent) CheckCommand(command string) Verdict

CheckCommand runs a command line through the deterministic red-line wall.

func (*Agent) Close

func (a *Agent) Close() error

Close releases the agent's LLM service, knowledge store, and MCP clients.

func (*Agent) Decide

func (a *Agent) Decide(id string, approve bool, reason string) error

Decide answers a held tool call: approve runs it with the arguments shown in its EventApproval; reject returns reason to the agent instead. It fails with ErrNoSuchApproval when nothing is waiting under id, and with an error when the agent was built without ApproveWrites.

func (*Agent) Diagnose

func (a *Agent) Diagnose(ctx context.Context, symptom string) (string, error)

Diagnose is Ask framed for troubleshooting a symptom.

func (*Agent) Doctor

func (a *Agent) Doctor(ctx context.Context) *Diagnosis

Doctor reports on the retrieval path: whether the embedder is reachable and unproxied, whether the index caps recall, how the corpus splits between code and prose, how much of the graph is unreachable, and how far the extracted graph has drifted from the vocabulary domain.toml declares.

Inventory answers "what is in the store"; this answers "does retrieval still work", which is the question that goes wrong silently. Every check exists because the corresponding failure produced no error anywhere — the copilot simply answered worse, and nothing said why. A caller embedding this library has no CLI to run `steward doctor` with, which is the whole reason it is here.

func (*Agent) Domain

func (a *Agent) Domain() *Domain

Domain returns the loaded product configuration.

func (*Agent) IngestDir

func (a *Agent) IngestDir(ctx context.Context, dir string) (IngestStats, error)

IngestDir walks a local directory into the knowledge base: docs (.md/.adoc/…) as prose, source files mined for their error/log strings. Document ids are prefixed with the directory's base name (the source name used by Refresh/PurgeSource).

func (*Agent) IngestDoc

func (a *Agent) IngestDoc(ctx context.Context, id, title, content string) error

IngestDoc adds or replaces a single document under the stable key id. When an LLM is configured it also extracts ontology entities/relations into the graph.

func (*Agent) Inventory

func (a *Agent) Inventory(ctx context.Context) (*Inventory, error)

Inventory reports what the knowledge base holds: which documents were ingested, how many chunks and graph entities they produced, and the vector width the index was actually built at.

The dimension is read back from the index rather than from configuration because those two disagreeing is a real failure mode with no other symptom: point the agent at a new embedder and every search quietly returns nothing.

func (*Agent) LLM

func (a *Agent) LLM() LLMSettings

LLM reports the model in force right now.

func (*Agent) MCPStatus

func (a *Agent) MCPStatus() []MCPStatus

MCPStatus returns the per-server outcome of mounting the configured MCP servers (whether each connected, how many tools were registered, and any error). It is empty when no MCPServers were configured.

func (*Agent) PurgeSource

func (a *Agent) PurgeSource(ctx context.Context, match string, prefix bool) (chunks, nodes int, err error)

PurgeSource removes a source's chunks (and derived graph nodes/edges). match is a document id, or its prefix when prefix is true. Returns the counts removed.

func (*Agent) Refresh

func (a *Agent) Refresh(ctx context.Context, dir string) (IngestStats, error)

Refresh purges everything previously ingested from dir (by its source name) and re-ingests it, so both updates and deletions take effect.

func (*Agent) ResolveSpellings

func (a *Agent) ResolveSpellings(ctx context.Context, dryRun bool) (*SpellingReport, error)

ResolveSpellings merges entities that are one concept spelled several ways.

Entity ids keep separators, so "DRBDResource" and "DRBD resource" are two nodes and each document's edges attach to whichever spelling it used. Walk pools them at query time; this ends the split in the graph itself, restricted to the types domain.toml declares so a code graph in the same store keeps its same-named files apart.

dryRun reports the merges without making them. Deleting a node is not reversible, so a caller should show the list before applying it.

func (*Agent) Search

func (a *Agent) Search(ctx context.Context, query string, topK int) ([]Hit, error)

Search returns the top reranked knowledge chunks for a query (no LLM call).

func (*Agent) SetLLM

func (a *Agent) SetLLM(s LLMSettings, apiKey string) (LLMSettings, error)

SetLLM changes the model the agent generates with, without a restart.

This exists because restarting is not a neutral act everywhere it runs: in SDS the agent is a unit in a drbd-reactor promoter's start list, so restarting it to pick up a new model name demotes the resource and fails the whole control plane over to another node. A model change should not cost an outage.

An empty field keeps the current value, which is what makes "switch the model but keep the gateway" a one-field edit and, more importantly, means a caller changing the model never has to resend the API key.

The new provider is built and only then swapped in, so a configuration that cannot be constructed leaves the working one running. What this CANNOT do is prove the endpoint answers — that takes a request, and a settings call is the wrong place to spend one. Callers that want certainty should ask a question afterwards; the swap is reported as done, not as verified.

In-flight requests finish on the provider they started with.

func (*Agent) Stream

func (a *Agent) Stream(ctx context.Context, sessionID, question string, on func(Event)) (answer string, sources []string, err error)

Stream runs a question in the conversation identified by sessionID and delivers Events to on as they happen (tool calls, answer deltas, …). It returns the full answer (with a deterministic Sources footer appended when the model didn't cite) and the sources retrieved this turn.

sessionID is what makes a follow-up a follow-up. Streaming used to drop it, so every turn of a streaming UI started from nothing: ask why a resource degraded, then ask "so how do I fix it", and the agent no longer knew what "it" was. Chat carried a session and Stream did not, which meant the mode a chat sidebar actually uses was the one without memory.

An empty sessionID runs the turn with no history, which is what a one-shot caller wants and what Ask does.

The model sometimes streams a short preamble, then delivers the real answer via its completion tool; when that happens Stream emits an EventReset before the authoritative answer so a UI can clear the preamble.

func (*Agent) Walk

func (a *Agent) Walk(ctx context.Context, entity, relation, direction string) (*WalkResult, error)

Walk follows the knowledge graph from a named entity along a relation, in a direction — WalkOut ("what does it back"), WalkIn ("what backs it") or WalkBoth.

This is the question retrieval cannot answer. Search finds text that is ABOUT something and returns whatever is one hop from it; "which pools back this resource" has an exact answer sitting in the graph, and routing it through a vector index means hoping the right chunk scores well AND that its neighbourhood contains the answer.

A relation whose ends the domain declared is walked through the ontology, so each result is a node of the far end's declared type — an edge contradicting the declaration is left out rather than returned as though it conformed. One with open ends follows the edges themselves, and says so.

type Approval

type Approval struct {
	ID     string         `json:"id"`
	Server string         `json:"server"`
	Tool   string         `json:"tool"`
	Args   map[string]any `json:"args"`
}

Approval is a mutating tool call held for an operator's decision. It is emitted as an EventApproval in the stream whose turn made the call; the call runs only once Agent.Decide approves it, with exactly these arguments.

type Check

type Check = knowledge.Check

Check is one diagnostic result within a Diagnosis.

type Config

type Config struct {
	LLMBaseURL string // STEWARD_LLM_BASE_URL (default https://api.openai.com/v1)
	LLMAPIKey  string // STEWARD_LLM_API_KEY (required for Ask/Diagnose/Chat/Stream)
	LLMModel   string // STEWARD_LLM_MODEL (default gpt-4o)

	EmbBaseURL string // STEWARD_EMB_BASE_URL (defaults to LLMBaseURL)
	EmbAPIKey  string // STEWARD_EMB_API_KEY (defaults to LLMAPIKey)
	EmbModel   string // STEWARD_EMB_MODEL (default text-embedding-3-small)
	EmbDim     int    // STEWARD_EMB_DIM (default 1536) — must match the index

	KnowledgeDBPath string // STEWARD_KNOWLEDGE_DB_PATH (default ./data/knowledge.db)
	// SharedKnowledgeDBPaths are read-only knowledge bases searched alongside
	// KnowledgeDBPath — product knowledge built once and installed as a file.
	// STEWARD_SHARED_KNOWLEDGE_DBS (comma-separated). Nothing is written to them.
	SharedKnowledgeDBPaths []string
	SessionDBPath          string // STEWARD_DB_PATH (default ./data/steward.db)

	// alchemy, the extraction service. With an address, Ingest reads prose
	// through alchemy: every node and edge carries provenance, and a
	// document whose sources contradict each other is held rather than
	// written. Without one, the built-in per-chunk LLM extractor reads it.
	AlchemyAddr  string // STEWARD_ALCHEMY_ADDR
	AlchemyToken string // STEWARD_ALCHEMY_TOKEN
	AlchemyTLS   bool   // STEWARD_ALCHEMY_TLS

	// Domain source. Set Domain for an in-memory config, or DomainFile for a path
	// to a domain.toml (STEWARD_DOMAIN_FILE if both are empty). Domain wins if set.
	DomainFile string
	Domain     *Domain

	// MCPServers are external MCP servers whose tools are mounted into the ReAct
	// loop. This is a code-only knob (no STEWARD_* fallback). A server that fails to
	// connect is skipped (the agent degrades to knowledge-only); inspect the
	// outcome with Agent.MCPStatus. Mark a spec ReadOnly to mount only its
	// observational tools — the safe default for cluster-control servers.
	MCPServers []MCPServerSpec

	// ApproveWrites holds every mutating MCP tool call for an operator's
	// decision. Stream surfaces the call as an EventApproval and waits for
	// Decide; a call rejected, left undecided past ApprovalTimeout, or made
	// outside Stream is not executed, and the agent is told so.
	ApproveWrites bool
	// ApprovalTimeout bounds the wait (default DefaultApprovalTimeout).
	ApprovalTimeout time.Duration
}

Config configures an Agent. Every zero-valued field falls back to its STEWARD_* environment variable and then a default (see the package docs).

type Diagnosis

type Diagnosis = knowledge.Diagnosis

Diagnosis is the retrieval-path health report. See Agent.Doctor.

type Domain

type Domain = domain.Domain

Domain is a product configuration: persona, ontology, error patterns, probes, red-lines, repos. Load one from TOML with LoadDomain.

func LoadDomain

func LoadDomain(path string) (*Domain, error)

LoadDomain reads a domain.toml from disk.

type Event

type Event struct {
	Kind       EventKind
	Text       string
	Tool       string
	Args       map[string]any
	Suggestion *Suggestion // set only for EventSuggestion
	Approval   *Approval   // set only for EventApproval
	Outcome    *Outcome    // set only for EventOutcome
}

Event is one item in a streamed run. For EventSuggestion, Suggestion is set (and Tool is "suggest_action"); for tool events Tool/Args are set; otherwise Text.

type EventKind

type EventKind string

EventKind classifies a streaming Event.

const (
	EventText       EventKind = "text"        // Text is an answer delta
	EventToolCall   EventKind = "tool_call"   // Tool + Args: a tool the agent invoked
	EventToolResult EventKind = "tool_result" // Tool: that tool returned
	EventReset      EventKind = "reset"       // discard answer text emitted so far (a preamble)
	EventError      EventKind = "error"       // Text is a non-fatal error message
	EventSuggestion EventKind = "suggestion"  // Suggestion: a structured, non-executed action proposal
	EventApproval   EventKind = "approval"    // Approval: a write tool call waiting for Decide
	EventOutcome    EventKind = "outcome"     // Outcome: how the turn ended — done, or blocked and why
)

type Hit

type Hit = knowledge.Hit

Hit is a retrieved knowledge chunk.

type IngestStats

type IngestStats = ingest.Stats

IngestStats summarizes an ingest run (doc/code files, mined error strings, skips).

type Inventory

type Inventory = knowledge.Inventory

Inventory describes what the knowledge base holds. See Agent.Inventory.

type LLMSettings

type LLMSettings struct {
	BaseURL string `json:"baseUrl"`
	Model   string `json:"model"`
}

LLMSettings is the model the agent generates with, as an operator sees it. The API key is never part of it — see Agent.llmAPIKey.

type LogReport

type LogReport = loganalyze.Report

LogReport is the result of triaging a log file/dir/archive.

type MCPServerSpec

type MCPServerSpec struct {
	Name              string            // logical name, e.g. "sds"
	Transport         string            // "stdio" | "http" | "sse" (default "stdio")
	Command           string            // stdio: executable
	Args              []string          // stdio args
	URL               string            // http/sse base URL
	Headers           map[string]string // http/sse optional headers
	ReadOnly          bool              // when true, mount only read-only tools
	ReadOnlyToolAllow []string          // explicit allowlist (optional; overrides the name heuristic)
	// WriteToolAllow names mutating tools to mount on a ReadOnly server as
	// well: "everything observational, plus these". ReadOnlyToolAllow cannot
	// say that — setting it replaces the read-only gate with the list, so
	// granting one write tool through it drops every read tool not also
	// named. These tools are mounted as mutations: never cached, never run
	// concurrently.
	WriteToolAllow []string
}

MCPServerSpec configures one external MCP server to mount into the agent.

type MCPStatus

type MCPStatus = agents.MCPMountStatus

MCPStatus reports the outcome of mounting one MCP server in New.

type OntologyDrift

type OntologyDrift = knowledge.OntologyDrift

OntologyDrift is where the extracted graph left the vocabulary domain.toml declares. Carried on Inventory.

type Outcome

type Outcome struct {
	Status string `json:"status"` // "complete" | "blocked"
	Text   string `json:"text,omitempty"`
}

Outcome is how a turn ended: the agent finished the task, or it stopped because something only the operator can resolve stood in the way. Text is the agent's own summary or blocker, empty when the answer already says it.

type SourceInfo

type SourceInfo = knowledge.SourceInfo

SourceInfo is one ingested document's footprint.

type SpellingGroup

type SpellingGroup = knowledge.SpellingGroup

SpellingGroup is one concept and the spellings folded into it.

type SpellingReport

type SpellingReport = knowledge.SpellingReport

SpellingReport is what an entity resolution did. See Agent.ResolveSpellings.

type Suggestion

type Suggestion struct {
	Action   string         `json:"action"`
	Params   map[string]any `json:"params"`
	Reason   string         `json:"reason"`
	Severity string         `json:"severity"` // "low" | "medium" | "high"
	Verdict  Verdict        `json:"verdict"`
}

Suggestion is a structured, NON-executed action the agent proposes for an operator to approve. It is emitted via EventSuggestion when the agent calls the built-in suggest_action tool; nothing is run. Verdict is the deterministic red-line wall's ruling on the proposed action (Verdict.Blocked ⇒ the action is destructive and needs explicit, guarded confirmation).

type Verdict

type Verdict = safety.Verdict

Verdict is the red-line wall's ruling on a command.

type WalkResult

type WalkResult = knowledge.WalkResult

WalkResult is what a graph walk found. See Agent.Walk.

type WalkStep

type WalkStep = knowledge.WalkStep

WalkStep is one node a walk reached.

Directories

Path Synopsis
cmd
steward command
Command steward is the CLI entry point for the steward ops & support agent.
Command steward is the CLI entry point for the steward ops & support agent.
examples
lib command
Command lib-example shows how to embed steward as a library.
Command lib-example shows how to embed steward as a library.
internal
agents
Package agents builds the ops agent: an agent-go service wired with a frontier LLM, a cortexdb-backed graph memory, the domain's read-only probe tools, a GraphRAG knowledge_search tool, and the deterministic red-line safety wall.
Package agents builds the ops agent: an agent-go service wired with a frontier LLM, a cortexdb-backed graph memory, the domain's read-only probe tools, a GraphRAG knowledge_search tool, and the deterministic red-line safety wall.
alchemyclient
Package alchemyclient reads prose through alchemy.
Package alchemyclient reads prose through alchemy.
apigraph
Package apigraph turns an OpenAPI 3 or Swagger 2 document into a graph: one node per operation and per schema an operation takes or returns, edges from each operation to those schemas, and — given the code graph and the command line graph — edges to the server function that implements the operation and from the commands that call it.
Package apigraph turns an OpenAPI 3 or Swagger 2 document into a graph: one node per operation and per schema an operation takes or returns, edges from each operation to those schemas, and — given the code graph and the command line graph — edges to the server function that implements the operation and from the commands that call it.
cite
Package cite renders source citations for grounded answers.
Package cite renders source citations for grounded answers.
cligraph
Package cligraph turns a command line's --help pages into a graph: one node per command, flag and positional argument, with the edges between them, and an edge from each command to the function that implements it when the repository's code graph has one.
Package cligraph turns a command line's --help pages into a graph: one node per command, flag and positional argument, with the edges between them, and an edge from each command to the function that implements it when the repository's code graph has one.
config
Package config loads runtime configuration from the environment.
Package config loads runtime configuration from the environment.
domain
Package domain is the platform's product-agnostic seam.
Package domain is the platform's product-agnostic seam.
extract
Package extract turns a text chunk into an ontology fragment (entities + relations) using an LLM, constrained to the entity/relation vocabulary of a Domain.
Package extract turns a text chunk into an ontology fragment (entities + relations) using an LLM, constrained to the entity/relation vocabulary of a Domain.
graphimport
Package graphimport loads an Understand-Anything knowledge-graph.json into the cortexdb knowledge base: node summaries become semantic vectors; nodes/edges become the ontology graph; and the architecture layers + guided tour are imported as their own nodes with membership edges.
Package graphimport loads an Understand-Anything knowledge-graph.json into the cortexdb knowledge base: node summaries become semantic vectors; nodes/edges become the ontology graph; and the architecture layers + guided tour are imported as their own nodes with membership edges.
httpapi
Package httpapi exposes the agent over HTTP: a thin JSON layer over the same agent.Service + knowledge.Store the CLI uses.
Package httpapi exposes the agent over HTTP: a thin JSON layer over the same agent.Service + knowledge.Store the CLI uses.
ingest
Package ingest walks a project's source/doc repository and feeds it into the knowledge base.
Package ingest walks a project's source/doc repository and feeds it into the knowledge base.
knowledge
Package knowledge is the GraphRAG knowledge base over a project's source and docs.
Package knowledge is the GraphRAG knowledge base over a project's source and docs.
loganalyze
Package loganalyze is a product-agnostic log triage engine.
Package loganalyze is a product-agnostic log triage engine.
objectmodel
Package objectmodel extracts a precise object model from a project's structured source-of-truth — the same idea as schemaimport (SQL DDL), generalized to the other shapes a project ships its data model in:
Package objectmodel extracts a precise object model from a project's structured source-of-truth — the same idea as schemaimport (SQL DDL), generalized to the other shapes a project ships its data model in:
probes
Package probes runs READ-ONLY diagnostic commands the agent may call during a ReAct loop.
Package probes runs READ-ONLY diagnostic commands the agent may call during a ReAct loop.
safety
Package safety is the platform's deterministic "red-line" interception mechanism.
Package safety is the platform's deterministic "red-line" interception mechanism.
salvage
Package salvage rescues an Understand-Anything run that produced intermediate output but never wrote a final knowledge-graph.json (the headless /understand step stalls on large repos).
Package salvage rescues an Understand-Anything run that produced intermediate output but never wrote a final knowledge-graph.json (the headless /understand step stalls on large repos).
scaffold
Package scaffold drafts a domain.toml for a new project from its repository, using an LLM.
Package scaffold drafts a domain.toml for a new project from its repository, using an LLM.
schemaimport
Package schemaimport extracts a precise object model from a SQL schema (DDL) into the knowledge graph: CREATE TABLE → entity, FOREIGN KEY → relation.
Package schemaimport extracts a precise object model from a SQL schema (DDL) into the knowledge graph: CREATE TABLE → entity, FOREIGN KEY → relation.
textcut
Package textcut shortens text for display without cutting a character in half.
Package textcut shortens text for display without cutting a character in half.
Package web embeds the built front-end (web/dist) so `steward serve`/`ui` ships the whole app as a single binary.
Package web embeds the built front-end (web/dist) so `steward serve`/`ui` ships the whole app as a single binary.

Jump to

Keyboard shortcuts

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