core

package
v1.4.15 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 11 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.

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 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 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.

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"
)

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)
	// 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 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 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 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)
}

AnalyzeTool is the contract every analyze-side read-only tool satisfies. Tools are registered with the analyze 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 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 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 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
	// 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 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

	// 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 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 ToolCallTrace added in v1.4.3

type ToolCallTrace struct {
	Name       string
	Args       string
	Output     string
	DurationMs int64
	Error      string
}

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

type ToolResult added in v1.4.3

type ToolResult struct {
	Tool  string         `json:"tool"`
	Found bool           `json:"found"`
	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 (pattern_history, describe_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).

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