core

package
v1.4.25 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// FieldService is the Signal.Fields key holding the discovered service
	// name (string). Empty when the source did not stamp one.
	FieldService = "service"
	// FieldSignal is the Signal.Fields key holding the logical signal name
	// (string) — e.g. a metric golden-signal or trace operation label.
	FieldSignal = "signal"
)

Canonical Signal.Fields keys shared across the agent pipeline. They name the two attributes every signal type can carry regardless of source: the discovered service and the logical signal name (a metric's golden-signal, a trace operation, or a log template label). They live in OSS so any consumer — the worker's learn-exclusion chokepoint, an enterprise metric/trace brain, a standing data source — keys off ONE definition. Enterprise packages (pkg/intel, pkg/datasource) keep their own copies to stay decoupled; a cross-package drift test pins them equal.

View Source
const (
	AnalyzeEventRunStarted    = "run_started"
	AnalyzeEventModelStarted  = "model_started"
	AnalyzeEventModelDelta    = "model_delta"
	AnalyzeEventModelFinished = "model_finished"
	AnalyzeEventToolStarted   = "tool_started"
	AnalyzeEventToolFinished  = "tool_finished"
	AnalyzeEventToolError     = "tool_error"
	AnalyzeEventRunFinished   = "run_finished"
	AnalyzeEventRunFailed     = "run_failed"
)

Analyze run event kinds. These are the wire values the UI switches on, so they are part of the API surface — renaming one breaks a client.

View Source
const (
	ChatEventRunStarted   = "run_started"
	ChatEventModelDelta   = "model_delta"
	ChatEventToolStarted  = "tool_started"
	ChatEventToolFinished = "tool_finished"
	ChatEventCompacted    = "compacted"
	ChatEventRunFinished  = "run_finished"
	ChatEventRunFailed    = "run_failed"
	ChatEventRunCancelled = "run_cancelled"
)

Variables

View Source
var CreateOnCallProvider func(cfg *config.Config, awsClient *ssmincidents.Client) (OnCallProvider, error)

Function that will be implemented in the common package to avoid circular imports

Functions

func CallerAuthorized added in v1.4.25

func CallerAuthorized(ctx context.Context, permission Permission) bool

CallerAuthorized returns false for absent/background callers and explicit denials.

func EmitAnalyzeEvent added in v1.4.24

func EmitAnalyzeEvent(ctx context.Context, ev AnalyzeEvent)

EmitAnalyzeEvent delivers ev to the observer on ctx, if any. It stamps At when the caller left it zero.

func EmitChatEvent added in v1.4.25

func EmitChatEvent(ctx context.Context, event ChatEvent)

EmitChatEvent stamps and delivers an event when an observer is attached.

func InitOnCallWorkflow

func InitOnCallWorkflow(awsClient *ssmincidents.Client, redisClient redis.UniversalClient)

InitOnCallWorkflow initializes the global singleton instance This is called once from main.go with the Redis client and AWS client

func IsOnCallWorkflowInitialized added in v1.4.4

func IsOnCallWorkflowInitialized() bool

IsOnCallWorkflowInitialized reports whether the global on-call workflow singleton has been initialized via InitOnCallWorkflow. Callers should check this before GetOnCallWorkflow so an uninitialized singleton is skipped instead of panicking the process.

func NewToolError added in v1.4.25

func NewToolError(code ToolErrorCode, message string, cause error) error

NewToolError constructs a classified tool error with a model-safe message.

func SetOnCallWorkflow added in v1.4.13

func SetOnCallWorkflow(w *OnCallWorkflow)

SetOnCallWorkflow installs (or, with a nil argument, clears) the global on-call workflow singleton directly, bypassing the one-shot boot path in InitOnCallWorkflow. It lets callers wire a pre-built workflow after startup (or reset it) without the sync.Once guard.

func ToolDisplayName added in v1.4.24

func ToolDisplayName(tool Tool) string

ToolDisplayName returns an explicit display name or title-cases the stable name.

func WithAnalyzeObserver added in v1.4.24

func WithAnalyzeObserver(ctx context.Context, obs AnalyzeObserver) context.Context

WithAnalyzeObserver attaches an observer to ctx. Passing it through the context rather than the task keeps AnalyzeTask a plain serialisable value.

func WithCallerAuthorization added in v1.4.25

func WithCallerAuthorization(ctx context.Context, authorization CallerAuthorization) context.Context

WithCallerAuthorization carries one caller decision into HTTP and model tools.

func WithChatObserver added in v1.4.25

func WithChatObserver(ctx context.Context, observer ChatObserver) context.Context

WithChatObserver attaches an observer to a chat run context.

Types

type AIAgent added in v1.4.3

type AIAgent interface {
	Name() string
	Kind() AITaskKind
	Run(ctx context.Context, task AITask) (*AICallResult, error)
}

AIAgent is one concrete model + prompt + (optional) tool wiring, dedicated to a single AITaskKind. Implementations live under pkg/agent/ai (e.g. detect, analyze).

Run is expected to be self-contained: it owns the model call, but the router handles cache / rate-limit / persistence around it.

type AICallResult added in v1.4.0

type AICallResult struct {
	Finding     *AIFinding
	UserPrompt  string
	RawResponse string
	DurationMs  int64
	Model       string

	// ToolCalls is populated by tool-using agents (analyze). It is
	// nil for tool-free agents (detect).
	ToolCalls []ToolCallTrace
}

AICallResult bundles the parsed finding with the inputs and outputs of the underlying model call. The trace fields (UserPrompt, RawResponse, DurationMs, Model) are persisted into the detect log so operators can audit what was sent to the model and what came back.

SystemPrompt is intentionally omitted — it is a constant per build and would bloat every record. Operators can fetch the current assembled system prompt via the agent admin API.

type AIFinding added in v1.3.9

type AIFinding struct {
	Title       string
	Summary     string
	Severity    string  // critical|high|medium|low
	Category    string  // e.g. "database", "auth", "deploy"
	Confidence  float64 // 0..1
	Suggestions []string
	SampleIDs   []string // signal IDs / pattern IDs for traceability

	// Analyze-mode fields. All optional; omitempty so detect-mode
	// payloads stay byte-compatible with the detect-only shape.
	RootCauseHypotheses []RootCauseHypothesis `json:"root_cause_hypotheses,omitempty"`
	Evidence            []EvidenceItem        `json:"evidence,omitempty"`
	RelatedPatternIDs   []string              `json:"related_pattern_ids,omitempty"`
	NextSteps           []string              `json:"next_steps,omitempty"`
}

AIFinding is the structured response returned by an AISRE for an unknown / spiking pattern. Used by detect mode.

Detect mode populates Title, Summary, Severity, Category, Confidence, Suggestions, SampleIDs. The lower block (RootCauseHypotheses, Evidence, RelatedPatternIDs, NextSteps) is populated by analyze mode only; detect leaves them empty and they marshal as omitempty.

type AITask added in v1.4.3

type AITask interface {
	Kind() AITaskKind
	CacheKey() string
}

AITask is the input to an AIAgent.Run call. Each concrete task type carries the inputs needed by its kind and exposes a CacheKey() the router uses for memoisation. An empty CacheKey disables caching for that call.

type AITaskKind added in v1.4.3

type AITaskKind string

AITaskKind identifies what an AIAgent is being asked to do.

The dispatcher uses the kind to pick the right agent, cache, and rate limiter. New kinds are added by extending this enum and the router's wiring.

const (
	// AITaskDetect is a cheap, tool-free, single-call classification of
	// an unknown or spiking log pattern. The output is an AIFinding
	// emitted as an incident through services.CreateIncidentFromFinding.
	AITaskDetect AITaskKind = "detect"

	// AITaskAnalyze is an operator-triggered, tool-using investigation
	// of a single incident. The output is an AIFinding persisted to the
	// analyses storage blob. Analyze NEVER fans out to notification
	// channels.
	AITaskAnalyze AITaskKind = "analyze"

	// AITaskChat is an operator-triggered conversational DevOps/SRE turn.
	// Its markdown result is persisted in a chat session and is never cached.
	AITaskChat AITaskKind = "chat"
)

type AgentResult added in v1.3.9

type AgentResult struct {
	Verdict           AgentVerdict
	PatternID         string   // empty when VerdictUnknown and pattern not yet stored
	Template          string   // human-readable pattern template ("Failed to ... at <*>:<*>")
	SampleSignals     []Signal // representative signals (capped, post-redaction)
	Frequency         int      // matches in the current tick / window
	Baseline          float64  // EWMA baseline for the pattern (0 when unknown)
	AgentKind         string   // emitting agent kind; "detect" for the detect worker
	SignalKind        string   // brain kind: logs | metrics | traces
	DetectionEmission *DetectionEmissionResult
	// RuleSeverity is the strongest operator-declared severity carried by the
	// grouped signals (e.g. an anomaly rule's `severity: critical`). Empty for
	// auto-discovered signals with no declared severity. It acts as a floor:
	// the AI may escalate but must not silently demote below it.
	RuleSeverity string
}

AgentResult is the output of a Detector for one batch of signals that share the same fingerprint.

type AgentVerdict added in v1.3.9

type AgentVerdict int

AgentVerdict is the classification a Detector pipeline assigns to a batch of signals that share a fingerprint.

const (
	// VerdictKnownPattern means the pattern is in the catalog and current
	// frequency is within baseline — suppress (no AI, no incident).
	VerdictKnownPattern AgentVerdict = iota
	// VerdictUnknown means the pattern was never seen during training —
	// forward to AI for analysis.
	VerdictUnknown
	// VerdictSpike means the pattern is known but frequency exceeds the
	// baseline by more than the configured threshold — forward to AI.
	VerdictSpike
)

func (AgentVerdict) String added in v1.3.9

func (v AgentVerdict) String() string

String renders an AgentVerdict for logging.

type Alert

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

func NewAlert

func NewAlert(providers ...AlertProvider) *Alert

func (*Alert) SendAlert

func (a *Alert) SendAlert(incident *m.Incident) error

SendAlert is the legacy "first error wins" API. New code should use SendAllAlerts so a flaky Slack does not silently mute Telegram and Email. Kept here so external callers (and existing tests) compile without churn; internally we now delegate to SendAllAlerts and surface only the joined error.

func (*Alert) SendAllAlerts added in v1.4.0

func (a *Alert) SendAllAlerts(incident *m.Incident) AlertResult

SendAllAlerts tries every configured provider regardless of earlier failures. A single broken channel never prevents the others from firing — this is the multi-channel promise. The returned Err is non-nil iff at least one provider failed.

type AlertProvider

type AlertProvider interface {
	Name() string
	SendAlert(incident *m.Incident) error
}

AlertProvider is implemented by every notification channel (Slack, Telegram, MS Teams, Lark, Viber, Email, …). Name() identifies the channel in failure reports so operators can tell which one failed without parsing the wrapped error.

type AlertResult added in v1.4.0

type AlertResult struct {
	Succeeded []string
	Failed    map[string]error
	Err       error // joined errors from Failed, nil when none
}

AlertResult captures the outcome of SendAllAlerts: which providers succeeded, which failed (with their error), and a joined error containing all failures (nil when every provider succeeded). The list of Succeeded channel names is what the incident persistence layer should store as ChannelsNotified — it is the truth about what actually reached its destination, not a list of what was enabled in config.

type AnalyzeEvent added in v1.4.24

type AnalyzeEvent struct {
	Seq         int64     `json:"seq"`
	At          time.Time `json:"at"`
	Kind        string    `json:"kind"`
	Tool        string    `json:"tool,omitempty"`
	ToolDisplay string    `json:"tool_display,omitempty"`
	Args        string    `json:"args,omitempty"`
	Output      string    `json:"output,omitempty"`
	DurationMs  int64     `json:"duration_ms,omitempty"`
	Error       string    `json:"error,omitempty"`
	// Turn is the model round-trip this event belongs to, starting at 1. The
	// ReAct loop alternates model turn -> tool calls -> model turn, so the UI
	// uses it to group tools under the turn that requested them.
	Turn int `json:"turn,omitempty"`
	// AnalysisID is set on the terminal event so the client can fetch the
	// persisted record without guessing which analysis it just watched.
	AnalysisID string `json:"analysis_id,omitempty"`
}

AnalyzeEvent is one observable step of an analyze run, emitted live while the ReAct loop is still working.

It carries the SAME already-capped, already-redacted strings that end up on the persisted ToolCallTrace — a live viewer never sees more than the stored record, so streaming opens no new egress path.

type AnalyzeIncidentSnapshot added in v1.4.3

type AnalyzeIncidentSnapshot struct {
	IncidentID string     `json:"incident_id"`
	Title      string     `json:"title,omitempty"`
	Service    string     `json:"service,omitempty"`
	Source     string     `json:"source,omitempty"`
	Severity   string     `json:"severity,omitempty"`
	Resolved   bool       `json:"resolved,omitempty"`
	CreatedAt  time.Time  `json:"created_at,omitempty"`
	AckedAt    *time.Time `json:"acked_at,omitempty"`
	ResolvedAt *time.Time `json:"resolved_at,omitempty"`

	// Content is the alert payload (the same map persisted on the
	// incident record). Operators see this verbatim in the UI.
	Content map[string]any `json:"content,omitempty"`

	// RequestedBy identifies the operator that triggered the analysis
	// (gateway-authenticated, so today this is just a free-form label
	// like "admin"). Stored on the AnalysisRecord for audit.
	RequestedBy string `json:"requested_by,omitempty"`
}

AnalyzeIncidentSnapshot is the input payload handed to the analyze-kind AIAgent. It captures the minimal incident metadata the agent needs to plan its tool calls; richer context (related logs, nearby incidents, pattern history) is fetched on demand via the agent's read-only tools, not pre-loaded into the snapshot.

The struct deliberately does not import pkg/storage — `pkg/core` must stay leaf-level. Callers (the admin controller, the worker) flatten storage records into this shape.

type AnalyzeObserver added in v1.4.24

type AnalyzeObserver interface {
	OnAnalyzeEvent(AnalyzeEvent)
}

AnalyzeObserver receives run events as they happen.

Implementations MUST NOT block: an observer is called from inside the agent's tool callbacks, so a slow or disconnected consumer would otherwise stall the model run itself.

func AnalyzeObserverFrom added in v1.4.24

func AnalyzeObserverFrom(ctx context.Context) AnalyzeObserver

AnalyzeObserverFrom returns the observer on ctx, or nil when the run is not being watched. Callers emit unconditionally via EmitAnalyzeEvent instead of nil-checking at every call site.

type AnalyzeTask added in v1.4.3

type AnalyzeTask struct {
	Snapshot AnalyzeIncidentSnapshot
}

AnalyzeTask wraps an on-demand analysis request. The snapshot carries the incident payload the agent inspects; richer context (related logs, pattern history) is fetched via the agent's read-only tools at run time, not pre-loaded here.

func (AnalyzeTask) CacheKey added in v1.4.3

func (t AnalyzeTask) CacheKey() string

CacheKey implements AITask. Empty disables caching — operators expect a fresh tool walk on every analyze request.

func (AnalyzeTask) Kind added in v1.4.3

func (AnalyzeTask) Kind() AITaskKind

Kind implements AITask.

type AnalyzeTool added in v1.4.3

type AnalyzeTool = Tool

AnalyzeTool is retained for source compatibility. Deprecated: use Tool.

type AnalyzeToolDisplayer added in v1.4.24

type AnalyzeToolDisplayer = ToolDisplayer

AnalyzeToolDisplayer is retained for source compatibility. Deprecated: use ToolDisplayer.

type Attachment added in v1.4.8

type Attachment struct {
	Filename string // "incident-<shortid>.png"
	MIME     string // "image/png"
	Data     []byte // the rendered image bytes
	Caption  string // short redacted text summary (also the fallback body)
}

Attachment is one rendered artifact (today, an incident report image) bound for a channel. Caption is the short text summary that image-capable channels attach alongside the binary AND that image-incapable channels fall back to. The Data bytes are already the final encoded image; the text is already redacted by the report assembler.

type AttachmentSender added in v1.4.8

type AttachmentSender interface {
	SendAttachment(incident *m.Incident, att Attachment) error
}

AttachmentSender is an OPTIONAL capability a notification channel may implement on top of the mandatory AlertProvider — exactly like storage.Searcher / storage.Lifecycle. A channel that can upload a binary image (Slack, Telegram, Email) implements it; a channel that cannot (Teams, Viber, Lark webhooks) simply does not, and the report delivery path detects the difference with a type assertion. The interface is deliberately generic: no per-channel special-casing leaks into the caller.

type Bucket added in v1.4.8

type Bucket struct {
	Label    string
	Count    int
	AIDetect int
	Webhook  int
}

Bucket is one labelled tally in an aggregate breakdown — a severity band, a trend time-slice, or a top service. Count is the total; AIDetect and Webhook carry the per-origin split so the trend timeline can draw a stacked bar (AIDetect + Webhook == Count). Labels are already redacted for the top-services list (a service name is attacker-influenced webhook content); the severity/trend labels are renderer-controlled constants.

type CallerAuthorization added in v1.4.25

type CallerAuthorization struct {
	Authenticated bool
	Permissions   map[Permission]bool
}

CallerAuthorization is a request-scoped, role-neutral permission decision.

type ChatAttachment added in v1.4.25

type ChatAttachment struct {
	Incident *ChatIncidentContext `json:"incident,omitempty"`
	Service  string               `json:"service,omitempty"`
	Time     *ChatTimeRange       `json:"time_range,omitempty"`
}

ChatAttachment grounds a turn without restricting the conversation to that context. Each field is optional and validated by the chat service.

type ChatCitation added in v1.4.25

type ChatCitation struct {
	Tool    string `json:"tool"`
	Label   string `json:"label,omitempty"`
	Locator string `json:"locator,omitempty"`
}

ChatCitation identifies evidence used by a conversational answer.

type ChatEvent added in v1.4.25

type ChatEvent struct {
	Seq         int64          `json:"seq"`
	At          time.Time      `json:"at"`
	Kind        string         `json:"kind"`
	Delta       string         `json:"delta,omitempty"`
	Tool        string         `json:"tool,omitempty"`
	CallID      string         `json:"call_id,omitempty"`
	ToolDisplay string         `json:"tool_display,omitempty"`
	Args        string         `json:"args,omitempty"`
	Output      string         `json:"output,omitempty"`
	DurationMs  int64          `json:"duration_ms,omitempty"`
	Error       string         `json:"error,omitempty"`
	Citations   []ChatCitation `json:"citations,omitempty"`
}

ChatEvent is one observable step of a chat turn. Error contains only a safe classification; backend and model errors must never cross this boundary.

type ChatIncidentContext added in v1.4.25

type ChatIncidentContext struct {
	ID       string    `json:"id"`
	Title    string    `json:"title,omitempty"`
	Service  string    `json:"service,omitempty"`
	Severity string    `json:"severity,omitempty"`
	Status   string    `json:"status,omitempty"`
	Created  time.Time `json:"created,omitempty"`
}

ChatIncidentContext is the bounded, redacted incident context that may be attached to a turn. Raw incident payloads are deliberately absent.

type ChatObserver added in v1.4.25

type ChatObserver interface {
	OnChatEvent(ChatEvent)
}

ChatObserver receives events synchronously. Implementations must return immediately, normally by using a bounded non-blocking channel send.

func ChatObserverFrom added in v1.4.25

func ChatObserverFrom(ctx context.Context) ChatObserver

ChatObserverFrom returns the observer attached to ctx, if any.

type ChatTask added in v1.4.25

type ChatTask struct {
	SessionID  string          `json:"session_id"`
	Message    string          `json:"message"`
	Attachment *ChatAttachment `json:"attachment,omitempty"`
}

ChatTask is one user turn in a durable chat session.

func (ChatTask) CacheKey added in v1.4.25

func (ChatTask) CacheKey() string

CacheKey implements AITask. Conversational turns are never replayable.

func (ChatTask) Kind added in v1.4.25

func (ChatTask) Kind() AITaskKind

Kind implements AITask.

type ChatTimeRange added in v1.4.25

type ChatTimeRange struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

ChatTimeRange is an absolute half-open interval attached to a chat turn. Natural-language date arithmetic is resolved before the model is called.

type ChatTurnAgent added in v1.4.25

type ChatTurnAgent interface {
	Name() string
	Kind() AITaskKind
	RunChatTurn(ctx context.Context, task ChatTask) (*ChatTurnResult, error)
}

ChatTurnAgent is the narrow execution contract implemented by the ADK chat agent. Its streaming, markdown-native result does not fit AIAgent.Run.

type ChatTurnResult added in v1.4.25

type ChatTurnResult struct {
	Markdown   string          `json:"markdown"`
	Citations  []ChatCitation  `json:"citations,omitempty"`
	ToolCalls  []ToolCallTrace `json:"tool_calls,omitempty"`
	DurationMs int64           `json:"duration_ms,omitempty"`
	Model      string          `json:"model,omitempty"`
}

ChatTurnResult is the markdown-native result of one chat turn. It is intentionally separate from AICallResult so chat is never coerced into an incident finding.

type DetectTask added in v1.4.3

type DetectTask struct {
	Result AgentResult
}

DetectTask wraps an AgentResult for detect-mode classification.

func (DetectTask) CacheKey added in v1.4.3

func (t DetectTask) CacheKey() string

CacheKey implements AITask. Detect memoisation is keyed by pattern id; an empty pattern id (unknown pattern not yet stored) disables caching for that call.

func (DetectTask) Kind added in v1.4.3

func (DetectTask) Kind() AITaskKind

Kind implements AITask.

type DetectionEmissionResult added in v1.4.25

type DetectionEmissionResult struct {
	Fingerprint         string
	EpisodeID           string
	IncidentID          string
	OccurrenceDelta     int64
	OccurrenceCount     int64
	EpisodeAction       string
	NotificationOutcome string
	EpisodeError        string
}

type Detector added in v1.3.9

type Detector interface {
	Name() string
	Process(ctx context.Context, batch []Signal) ([]AgentResult, error)
}

Detector consumes signals and emits AgentResults.

type Embedder added in v1.4.4

type Embedder interface {
	// Embed returns one vector per input string, in the same order. An
	// empty input yields an empty (non-nil) slice and a nil error.
	Embed(ctx context.Context, texts []string) ([][]float32, error)
}

Embedder is the leaf-level contract for turning text into vector embeddings. It is the single embeddings seam every RAG-bearing agent in the suite reuses (the runbook-RAG find_runbook tool is the first consumer). Like AnalyzeTool / ToolResult, it lives in pkg/core so the tools layer can depend on it without importing the concrete Eino wrapper (pkg/agent/ai/eino) that constructs it.

Implementations are an external trust boundary: callers MUST scrub any incident-derived text through the redactor before passing it to Embed, exactly as the analyze tools scrub log lines before the chat-completion call. The runbook corpus that is embedded at ingestion time is operator-authored content (an accepted boundary, documented in the runbook-RAG docs).

type EvidenceItem added in v1.4.3

type EvidenceItem struct {
	Source  string `json:"source"`
	Summary string `json:"summary"`
	Detail  string `json:"detail,omitempty"`
}

EvidenceItem records one piece of supporting evidence the analyze agent gathered (typically from a tool call). Source identifies where it came from (e.g. "tool:get_related_logs"); Summary is a one-liner shown in the UI; Detail is the long form (capped on persist).

type NotableIncident added in v1.4.8

type NotableIncident struct {
	ShortID   string
	Title     string
	Service   string
	Severity  string
	CreatedAt time.Time
}

NotableIncident is one row in the "recent high-severity" notable list. Every string field is ALREADY redacted by the assembler — the renderer draws it verbatim and never reaches back into raw content.

type Observation added in v1.4.4

type Observation struct {
	Key       string    // stable per-type fingerprint
	Service   string    // attribution (feeds grace + routing)
	Signal    string    // human label: template | "latency_p99" | "GET /checkout error_rate"
	Timestamp time.Time // observation time (seasonality needs it)
	Value     float64   // the scalar the learner folds + the detector scores
	Frequency int       // tick count (logs) / sample count (metrics/traces)
	Samples   []Signal  // representative raw signals for the AI-analyze handoff
	// StrongestSeverity is computed across the full grouped observation before
	// Samples is bounded. It is evidence metadata, never part of identity.
	StrongestSeverity string
	// IsNew reports whether Group saw this Key for the first time during this
	// tick (a freshly discovered template / metric / operation). It is used
	// only for discovery logging and never affects classification. (Additive
	// to the design-doc struct.)
	IsNew bool
}

Observation is one keyed unit a typed pipeline learns/scores for a tick.

logs    → one drain-template bucket            (Value = tick frequency)
metrics → one (service, golden-signal) window  (Value = latest sample / p99 / error-ratio)
traces  → one (service, operation) window       (Value = window p99 latency or error-ratio)

type OnCallProvider added in v1.3.0

type OnCallProvider interface {
	TriggerOnCall(ctx context.Context, incidentID string, cfg *config.OnCallConfig) error
}

OnCallProvider defines the interface for on-call notification providers

type OnCallWorkflow

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

OnCallWorkflow coordinates on-call escalation with a single provider

func GetOnCallWorkflow

func GetOnCallWorkflow() *OnCallWorkflow

GetOnCallWorkflow returns the global singleton instance This maintains compatibility with existing code

func NewOnCallWorkflow added in v1.3.0

func NewOnCallWorkflow(redisClient redis.UniversalClient, provider OnCallProvider) *OnCallWorkflow

NewOnCallWorkflow creates a new on-call workflow with the given provider

func (*OnCallWorkflow) Ack

func (w *OnCallWorkflow) Ack(incidentID string) error

Ack acknowledges an incident to prevent escalation

func (*OnCallWorkflow) Start

func (w *OnCallWorkflow) Start(incidentID string, oc config.OnCallConfig) error

Start initiates the on-call workflow for an incident

type Permission added in v1.4.25

type Permission string

Permission is an application capability evaluated for the current caller.

const PermissionInfrastructureView Permission = "infrastructure:view"

type QueueListener

type QueueListener interface {
	StartListening(handler func(content *map[string]interface{}) error) error
}

type Readiness added in v1.4.7

type Readiness struct {
	// Ready reports the signal has reached its settled/known state.
	Ready bool `json:"ready"`
	// Seen is the evidence folded so far (log sightings / folded samples).
	Seen int `json:"seen"`
	// Needed is the evidence required to reach Ready. 0 is the INDETERMINATE
	// sentinel: no count gate applies for this signal kind. The UI shows
	// "Manual only", never "X of 0". (Log patterns always ship a positive
	// Needed — a non-positive auto_promote_after is normalized to the default.)
	Needed int `json:"needed"`
	// RatePerMin is the observed arrival rate of NEW evidence for this key,
	// in evidence/minute, used to derive the ETA. 0 is the UNKNOWN/STALLED
	// sentinel: no honest rate yet, or the signal stopped flowing — the UI
	// shows no ETA (never ∞).
	RatePerMin float64 `json:"rate_per_min"`
}

Readiness is how close a signal is to its settled/known state — the point at which the agent's judgment of it is final rather than provisional. It is a generic, type-agnostic value produced at each read boundary (the log catalog reader in OSS, the enterprise metric/trace baseline reader). It is a plain value type with no behaviour: presentation (remaining evidence, ETA, progress bar) is DERIVED from these facts by the UI — the server ships facts, not formatted strings.

Derivations the UI does: remaining = max(0, Needed-Seen) (when Needed>0); etaMinutes = remaining / RatePerMin (when RatePerMin>0, Needed>0, !Ready); progress = Seen/Needed (when Needed>0).

type ReportImage added in v1.4.8

type ReportImage struct {
	Data     []byte // encoded image bytes (PNG)
	MIME     string // "image/png"
	Filename string // e.g. "incidents-today-20260704.png"
	Width    int
	Height   int
}

ReportImage is a rendered report: the encoded image bytes plus enough metadata for a channel to upload it or an HTTP handler to serve it.

type ReportModel added in v1.4.8

type ReportModel struct {
	// Window identity. Timestamps are expressed in TZLabel's location (UTC by
	// default), so a renderer/caption can print them verbatim.
	Window      string    // "today" | "24h" | "7d"
	WindowStart time.Time // inclusive
	WindowEnd   time.Time // exclusive (== GeneratedAt for today/24h)
	GeneratedAt time.Time
	// TZLabel is the IANA name of the timezone WindowStart/WindowEnd/
	// GeneratedAt are expressed in (e.g. "UTC" or "Asia/Ho_Chi_Minh"). The
	// renderer and channel caption print it beside the times so a reader knows
	// the wall-clock zone. Empty is treated as "UTC".
	TZLabel string

	// Title is the header/caption heading. It is operator-configurable via
	// the report settings; an empty value is treated as "Incident report" so a
	// directly-constructed model still renders a title.
	Title string

	// Headline stats.
	Total        int            // incidents in the window
	ByOrigin     map[string]int // ai_detect / webhook counts (primary category axis)
	Resolved     int
	Open         int
	CriticalHigh int // count of critical + high severity incidents

	// Chart series (hand-drawn, no chart lib).
	BySeverity []Bucket // ordered critical..unknown (category breakdown)
	Trend      []Bucket // hourly (today/24h) or daily (7d) counts
	TrendUnit  string   // "hour" | "day" — labels the trend axis

	// Notable lists (bounded, redacted labels).
	TopServices []Bucket          // top-N services by count
	Notable     []NotableIncident // recent high-severity incidents

	// IncludeCharts mirrors the runtime include_chart toggle: when false the
	// renderer omits the trend + severity charts (headline + lists remain).
	IncludeCharts bool

	// Footer mark. The OSS card always carries "Versus Incident"; the
	// enterprise white-label renderer overrides it behind the same seam.
	Footer string
}

ReportModel is the channel-agnostic, ALREADY-REDACTED aggregate summary of every incident in a time window. It is assembled by querying the incident history over the window (both ai_detect and webhook) and handed to a ReportRenderer. Every string field has already passed through a Scrubber, so a renderer draws it verbatim and must never reach back into raw incident content. No AckURL, token, or config value is ever placed here.

type ReportRenderer added in v1.4.8

type ReportRenderer interface {
	Render(ctx context.Context, m ReportModel) (*ReportImage, error)
}

ReportRenderer turns a ReportModel into a ReportImage. The OSS build wires the default pure-Go PNG card renderer at boot via services.SetReportRenderer; an enterprise build may install a branded / high-fidelity renderer behind the SAME seam without the API, channel delivery, or UI changing. Mirrors the SetAnalyzeAgent seam.

type RootCauseHypothesis added in v1.4.3

type RootCauseHypothesis struct {
	Hypothesis string  `json:"hypothesis"`
	Confidence float64 `json:"confidence"`
	Rationale  string  `json:"rationale,omitempty"`
}

RootCauseHypothesis is one candidate explanation produced by an analyze-mode agent. Confidence is in [0, 1]; Rationale is a short human-readable justification.

type Scrubber added in v1.4.8

type Scrubber interface {
	Scrub(s string) string
}

Scrubber is the minimal DLP contract the report path depends on: turn a possibly-sensitive string into a safe one. It is satisfied by agent.Redactor.Scrub, so the report assembler can scrub every field WITHOUT the services/report layers importing the heavier agent package. Every text value drawn onto a shared report image is run through a Scrubber first — defence-in-depth, because webhook incident Content is attacker-influenced input.

type Signal added in v1.3.9

type Signal struct {
	Source    string // e.g. "elasticsearch:prod-app"
	Timestamp time.Time
	Severity  string                 // raw severity from source, best-effort
	Message   string                 // primary text payload
	Fields    map[string]interface{} // structured fields
	Raw       map[string]interface{} // original document (capped size)
}

Signal is one normalized observation pulled from a SignalSource.

Fields holds best-effort structured fields; Raw is the original source document (capped to AgentConfig.SignalMaxBytes by the SignalSource so the pipeline cannot OOM on large documents).

type SignalDetector added in v1.4.4

type SignalDetector interface {
	// Kind identifies the signal type and must match its paired Learner.
	Kind() string

	// Classify scores one observation against the learned expectation
	// (mean, std, confident) captured by SignalLearner.Expected.
	Classify(obs Observation, mean, std float64, confident bool) TypedVerdict
}

SignalDetector is the pure SCORING POLICY: a function of (observation, learned expectation) → verdict. The per-type knob (z threshold, spike multiplier, sustained-tick debounce) lives here, not in the worker. The detector scores against the PASSED expectation (the pre-fold snapshot), never by re-reading the learner, so the result is independent of whether the worker has already folded the tick.

type SignalLearner added in v1.4.4

type SignalLearner interface {
	// Kind identifies the signal type: "logs" | "metrics" | "traces".
	Kind() string

	// Group keys a tick's batch into observations WITHOUT mutating the model.
	Group(ctx context.Context, batch []Signal) ([]Observation, error)

	// Expected returns the learned mean/spread for a key at ts, and whether
	// the model is confident enough to score (the per-key training gate).
	// It must be a pure read of pre-fold state: the worker snapshots it
	// BEFORE Learn folds the tick, so a deviation is judged against the
	// pre-tick baseline.
	Expected(ctx context.Context, key string, ts time.Time) (mean, std float64, confident bool)

	// Learn folds observations into the model. Called every tick in EVERY
	// mode (training/shadow/detect) so the baseline keeps adapting to drift.
	Learn(ctx context.Context, obs []Observation) error
}

SignalLearner owns the per-type LEARNED MODEL: keying + folding + the "what is normal" estimate. Implementations persist their derived state via the model-state seam (opaque bytes, keyed by OrgID). One implementation per signal type (logs in OSS; metrics/traces in Versus Enterprise).

type SignalSource added in v1.3.9

type SignalSource interface {
	// Name uniquely identifies the configured source instance. It is used as
	// part of Redis cursor keys, so it must be stable across restarts.
	Name() string

	// Pull returns all signals strictly newer than `since`, plus the new
	// cursor (== max timestamp seen, or `since` if no signals were returned).
	//
	// Tailing invariant: the returned cursor MUST NOT exceed the wall-clock
	// time at pull, and a cursor-driven source MUST NOT scan past `now`.
	// Document timestamps are untrusted producer data; a single future-dated
	// record would otherwise push the cursor ahead of real time and make every
	// following `>= cursor` query match nothing until that time arrives — the
	// source stops emitting until its cursor is wiped. Sources bound their scan
	// at `now` (Loki `end`, Graylog `to`, Splunk `latest`, Elasticsearch `lte`,
	// CloudWatch `EndTime`) and clamp the returned cursor via
	// signalsources.ClampCursor.
	Pull(ctx context.Context, since time.Time) ([]Signal, time.Time, error)
}

SignalSource is a puller for one external observability backend (Elasticsearch, Loki, CloudWatch, ...).

type SourceCommitter added in v1.4.23

type SourceCommitter interface {
	Commit(ctx context.Context) error
}

SourceCommitter is the OPTIONAL capability a SignalSource implements when a Pull leaves behind DELIVERY STATE — its own record of which rows it has already handed over — that must not become durable before the signals it describes are durable.

A tailing source dedupes the rows its inclusive re-read window returns twice using a set of the ids it already delivered. Persisting that set at the end of Pull is one write too early: the worker has not folded those rows into the catalog yet, and the catalog reaches storage only on its own flush interval. A process that dies in between comes back with the ids recorded as delivered and the rows themselves absent — silent, permanent loss.

So Pull only STAGES the delivery state in memory (enough to suppress the next tick's re-read inside the same process) and the worker calls Commit once the rows are durable: after a successful catalog flush, and again after the shutdown flush. The failure direction is deliberate and asymmetric — a crash between the flush and Commit re-delivers rows (bounded duplicates), a crash before the flush leaves the ids uncommitted so the next scan re-reads them (no loss). Duplicates are recoverable; dropped rows are not.

The ctx passed to Commit is always live, including on the shutdown path where the worker's own context has already been canceled. Sources with no staged state do not implement this and are skipped, so they behave exactly as they did before the seam existed. Implementations must be safe to call concurrently with Pull.

type SourceRewinder added in v1.4.7

type SourceRewinder interface {
	Rewind(ctx context.Context) error
}

SourceRewinder is the OPTIONAL capability a SignalSource implements when it keeps its OWN internal read position — a byte offset, page token, or similar — that is INDEPENDENT of the poll cursor the worker passes to Pull as `since`. The file source is the canonical example: it tails a log file by byte offset (persisted in a sidecar) and ignores `since` entirely.

Clearing the learned catalog rewinds the worker's poll cursor, which makes cursor-driven sources (Elasticsearch, Loki, CloudWatch, …) re-read their lookback window on the next tick. A source that tracks its own position must additionally rewind THAT position here — otherwise the clear leaves it pinned past the already-consumed data and it never re-emits, so the SAME running worker stops learning until the process is recreated (which reconstructs the source from scratch). Rewind reconciles the two cursors of truth so a clear behaves like a fresh process start in-place.

Sources whose position is driven purely by `since` do not implement it; the worker skips them. Implementations must be safe to call concurrently with Pull and must leave the source in the state a freshly-constructed instance would have.

type TextSender added in v1.4.8

type TextSender interface {
	SendText(incident *m.Incident, text string) error
}

TextSender is the OPTIONAL text-fallback sibling of AttachmentSender. A channel that cannot upload a binary but CAN post text (Teams, Viber, Lark webhooks) implements it so the report delivery path can still deliver the already-redacted caption + a short note to that channel. Like AttachmentSender it is generic and detected via type assertion; a channel that implements neither simply receives no report and the caller records a graceful fallback outcome.

type Tool added in v1.4.25

type Tool interface {
	// Name is the model-visible tool name. Must be a stable identifier
	// (snake_case is the convention).
	Name() string
	// Description is the one-line model-visible doc. The model uses it
	// to decide when to call the tool.
	Description() string
	// ArgsSchema returns a JSON schema (drafted as a generic map) for
	// the tool's argument object. Eino converts this into the model's
	// tool-call schema.
	ArgsSchema() map[string]any
	// Invoke runs the tool with the model-provided JSON args. The
	// returned ToolResult is serialised to JSON and fed back to the
	// model as the tool message. Errors are surfaced to the model as
	// a tool error so it can adapt.
	Invoke(ctx context.Context, args json.RawMessage) (*ToolResult, error)
}

Tool is the contract every read-only AI tool satisfies. Tools are registered with an agent at construction time; the agent surfaces them to the model as Eino ToolInfo and dispatches model-requested calls back to this interface.

Implementations MUST be read-only. The compile-time guard in pkg/agent/ai/analyze rejects any import of services.CreateIncident transitively.

type ToolCallTrace added in v1.4.3

type ToolCallTrace struct {
	CallID     string `json:"call_id,omitempty"`
	Name       string
	Args       string
	Output     string
	DurationMs int64
	Error      string
}

ToolCallTrace is one model-issued tool round-trip captured for audit.

type ToolDisplayer added in v1.4.25

type ToolDisplayer interface {
	DisplayName() string
}

ToolDisplayer optionally gives a tool a human-readable activity name.

type ToolError added in v1.4.25

type ToolError struct {
	Code    ToolErrorCode
	Message string
	Cause   error
}

ToolError carries a model-safe message while retaining the underlying cause for trusted server-side handling. Cause is never part of the model envelope.

func (*ToolError) Error added in v1.4.25

func (err *ToolError) Error() string

Error implements error without exposing the underlying cause.

func (*ToolError) Unwrap added in v1.4.25

func (err *ToolError) Unwrap() error

Unwrap retains errors.Is/errors.As support for trusted server-side callers.

type ToolErrorCode added in v1.4.25

type ToolErrorCode string

ToolErrorCode classifies a tool failure without exposing its underlying cause.

const (
	ToolErrorInvalidArguments ToolErrorCode = "invalid_arguments"
	ToolErrorTimeout          ToolErrorCode = "timeout"
	ToolErrorCancelled        ToolErrorCode = "cancelled"
	ToolErrorBackend          ToolErrorCode = "backend_error"
	ToolErrorInternal         ToolErrorCode = "internal_error"
)

func ClassifyToolError added in v1.4.25

func ClassifyToolError(err error) (ToolErrorCode, string)

ClassifyToolError returns only model-safe fields. Unknown errors are treated as backend failures and their text is deliberately discarded.

type ToolResult added in v1.4.3

type ToolResult struct {
	Tool      string         `json:"tool"`
	Found     bool           `json:"found"`
	Available *bool          `json:"-"`
	Reason    string         `json:"reason,omitempty"`
	Data      map[string]any `json:"data,omitempty"`
}

ToolResult is the uniform envelope every AnalyzeTool returns. The shape is stable so the model sees a predictable schema across tools; the per-tool payload lives in Data as JSON-encodable values.

Tool — the tool name (mirrors AnalyzeTool.Name) so a model parsing multiple tool responses can disambiguate without relying on call ordering.

Found — optional flag for lookup-style tools (get_pattern, get_service) to signal "no such entity" without an error. Defaults to true; lookups that miss should set it to false and leave Data empty (or populated with just the query echo).

Data — the typed payload. Keys are tool-specific; values must be JSON-marshalable (no channels, funcs, or unexported structs).

func UnavailableToolResult added in v1.4.25

func UnavailableToolResult(tool, reason string) *ToolResult

UnavailableToolResult returns an expected missing-capability result. UnavailableToolResult reports missing capability as a successful result so the model can adapt without treating expected configuration as a tool error.

func (ToolResult) IsAvailable added in v1.4.25

func (result ToolResult) IsAvailable() bool

IsAvailable reports availability, defaulting existing results to true.

func (ToolResult) MarshalJSON added in v1.4.25

func (result ToolResult) MarshalJSON() ([]byte, error)

MarshalJSON keeps availability explicit on the wire while preserving old constructors.

func (*ToolResult) UnmarshalJSON added in v1.4.25

func (result *ToolResult) UnmarshalJSON(data []byte) error

UnmarshalJSON preserves explicit availability when reading stored results.

type TypedVerdict added in v1.4.4

type TypedVerdict struct {
	Class     AgentVerdict // reuse the enum: VerdictKnownPattern | VerdictUnknown | VerdictSpike
	Confident bool         // false ⇒ this key is still TRAINING ⇒ lifecycle suppresses, any mode
	Score     float64      // z-score (metrics/traces) or spike ratio (logs)
	Baseline  float64      // learned expected value (for the finding text)
	Reason    string       // "p99 4.2σ above the learned Tue-14:00 baseline (≈ 180ms)"
}

TypedVerdict is the per-type classification of one observation.

Jump to

Keyboard shortcuts

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