agent

package
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package agent implements the core agent loop.

Index

Constants

View Source
const (
	KindToken         = llm.KindToken
	KindToolCallStart = llm.KindToolCallStart
	KindToolCallDone  = llm.KindToolCallDone
	KindDone          = llm.KindDone
)

Stream event kinds re-exported for consumers.

Variables

This section is empty.

Functions

This section is empty.

Types

type ChannelProvider added in v0.7.0

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

ChannelProvider is a ContextProvider that injects channel-specific context into the system prompt based on the "source" routing hint attached to the request context. When a ContactLookup is configured, it resolves sender names to contact records and injects a structured JSON contact profile alongside the channel notes.

func NewChannelProvider added in v0.7.0

func NewChannelProvider(contacts ContactLookup) *ChannelProvider

NewChannelProvider creates a channel awareness context provider. The contacts parameter is optional — pass nil to disable contact resolution (the provider will still emit channel notes).

func (*ChannelProvider) GetContext added in v0.7.0

func (p *ChannelProvider) GetContext(ctx context.Context, _ string) (string, error)

GetContext returns a channel context block if the request context carries a "source" hint that matches a known channel. When a contact lookup is available and the sender resolves to a known contact, the block includes a structured JSON contact profile with trust policy, communication channels, and interaction history. Returns an empty string for unrecognized sources or missing hints.

type Compactor

type Compactor interface {
	NeedsCompaction(conversationID string) bool
	Compact(ctx context.Context, conversationID string) error
}

Compactor handles conversation compaction.

type CompositeContextProvider

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

CompositeContextProvider combines multiple context providers. Each provider's output is concatenated with newlines.

func NewCompositeContextProvider

func NewCompositeContextProvider(providers ...ContextProvider) *CompositeContextProvider

NewCompositeContextProvider creates a composite from multiple providers.

func (*CompositeContextProvider) Add

func (c *CompositeContextProvider) Add(provider ContextProvider)

Add appends a provider to the composite.

func (*CompositeContextProvider) GetContext

func (c *CompositeContextProvider) GetContext(ctx context.Context, userMessage string) (string, error)

GetContext calls all providers and combines their output.

type ContactContext added in v0.8.0

type ContactContext struct {
	ID              string           `json:"id,omitempty"`
	Name            string           `json:"name"`
	GivenName       string           `json:"given_name,omitempty"`
	FamilyName      string           `json:"family_name,omitempty"`
	TrustZone       string           `json:"trust_zone"`
	TrustPolicy     *TrustPolicyView `json:"trust_policy"`
	Groups          []string         `json:"groups,omitempty"`
	Org             *string          `json:"org,omitempty"`
	Title           *string          `json:"title,omitempty"`
	Role            *string          `json:"role,omitempty"`
	Summary         string           `json:"summary,omitempty"`
	Related         []RelatedContact `json:"related,omitempty"`
	Channels        map[string]any   `json:"channels,omitempty"`
	LastInteraction *InteractionRef  `json:"last_interaction,omitempty"`
	ContactSince    string           `json:"contact_since,omitempty"`
}

ContactContext holds the rich contact profile injected into the system prompt as structured JSON. Fields are populated by the ContactLookup implementation and gated by the contact's trust zone — lower-trust zones receive fewer fields.

type ContactLookup added in v0.7.0

type ContactLookup interface {
	LookupContact(name string, source string) *ContactContext
}

ContactLookup resolves a contact name to a rich context profile for system prompt injection. The source parameter identifies the channel (e.g., "signal", "email") so the implementation can gate fields by trust zone. Returns nil when no matching contact is found.

type ContextProvider

type ContextProvider interface {
	// GetContext returns context to inject into the system prompt.
	// The userMessage is provided to enable semantic search for relevant knowledge.
	GetContext(ctx context.Context, userMessage string) (string, error)
}

ContextProvider supplies dynamic context for the system prompt.

type FailoverHandler

type FailoverHandler interface {
	// OnFailover is called when switching from one model to another due to failure.
	// Returns an error if failover should be aborted.
	OnFailover(ctx context.Context, fromModel, toModel, reason string) error
}

FailoverHandler is called before model failover to allow checkpointing.

type InteractionRef added in v0.8.0

type InteractionRef struct {
	AgoSeconds int64    `json:"ago_seconds"`
	Channel    string   `json:"channel,omitempty"`
	SessionID  string   `json:"session_id,omitempty"`
	Topics     []string `json:"topics,omitempty"`
}

InteractionRef summarizes the contact's most recent interaction for temporal context. AgoSeconds is negative for past interactions.

type Loop

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

Loop is the core agent execution loop.

func NewLoop

func NewLoop(logger *slog.Logger, mem MemoryStore, compactor Compactor, rtr *router.Router, ha *homeassistant.Client, sched *scheduler.Scheduler, llmClient llm.Client, defaultModel, talents, persona string, contextWindow int) *Loop

NewLoop creates a new agent loop.

func (*Loop) ActiveTags added in v0.7.0

func (l *Loop) ActiveTags() map[string]bool

ActiveTags returns a snapshot of the currently active capability tags. Returns nil when capability tagging is not configured. The returned map is a copy — callers may read it without holding any lock.

func (*Loop) CheckpointSession added in v0.7.0

func (l *Loop) CheckpointSession(conversationID, label string) error

CheckpointSession archives a snapshot of the current conversation state without ending the session. The active session continues uninterrupted.

func (*Loop) CloseSession added in v0.7.0

func (l *Loop) CloseSession(conversationID, reason, carryForward string) error

CloseSession gracefully closes the current session, archives messages, injects a carry-forward handoff into the new session, and starts a fresh session. Unlike ResetConversation, the carry-forward summary provides continuity across the session boundary.

func (*Loop) ConversationTranscript added in v0.7.1

func (l *Loop) ConversationTranscript(conversationID string) string

ConversationTranscript returns a formatted text transcript of the current in-memory conversation for the given ID. System and tool messages are excluded to focus on user/assistant dialogue. Returns an empty string if no user/assistant messages exist after filtering (for example, when there are no messages or only system/tool messages). The output is capped at [maxTranscriptBytes] to keep downstream LLM prompts within reasonable context limits.

func (*Loop) DropCapability added in v0.7.0

func (l *Loop) DropCapability(tag string) error

DropCapability deactivates a capability tag for the current session. Always-active and channel-pinned tags cannot be dropped. Returns an error if the tag is unknown, always active, or pinned by the current channel.

func (*Loop) GetContextWindow

func (l *Loop) GetContextWindow() int

GetContextWindow returns the context window size of the default model.

func (*Loop) GetHistory

func (l *Loop) GetHistory(conversationID string) []memory.Message

GetTokenCount returns the estimated token count for a conversation. GetHistory returns the conversation messages for a given conversation.

func (*Loop) GetTokenCount

func (l *Loop) GetTokenCount(conversationID string) int

func (*Loop) MemoryStats

func (l *Loop) MemoryStats() map[string]any

MemoryStats returns current memory statistics.

func (*Loop) Process

func (l *Loop) Process(ctx context.Context, conversationID, message string) (string, error)

Process is a convenience wrapper for single-shot requests (no streaming).

func (*Loop) RequestCapability added in v0.7.0

func (l *Loop) RequestCapability(tag string) error

RequestCapability activates a capability tag for the current session. Returns an error if the tag is unknown.

func (*Loop) ResetConversation

func (l *Loop) ResetConversation(conversationID string) error

ResetConversation archives and then clears the conversation history.

func (*Loop) Router added in v0.6.0

func (l *Loop) Router() *router.Router

Router returns the model router, or nil if no router is configured.

func (*Loop) Run

func (l *Loop) Run(ctx context.Context, req *Request, stream StreamCallback) (resp *Response, err error)

Run executes one iteration of the agent loop. If stream is non-nil, tokens are pushed to it as they arrive.

func (*Loop) SetArchiver added in v0.3.0

func (l *Loop) SetArchiver(archiver SessionArchiver)

SetArchiver configures the session archiver for preserving conversations.

func (*Loop) SetCapabilityTags added in v0.7.0

func (l *Loop) SetCapabilityTags(capTags map[string]config.CapabilityTagConfig, parsedTalents []talents.Talent)

SetCapabilityTags configures tag-driven tool and talent filtering. Tags marked always_active are activated immediately. The method also builds the tool registry's tag index and stores parsed talents for per-run filtering. When capTags is nil or empty, capability tagging is disabled and all tools/talents load unconditionally.

func (*Loop) SetChannelTags added in v0.8.0

func (l *Loop) SetChannelTags(ct map[string][]string)

SetChannelTags configures channel-pinned tag activation. When a Run() request carries a "source" hint matching a key in channelTags, the listed capability tags are activated for that run in addition to any always-active or agent-requested tags. Channel-pinned tags are ref-counted per concurrent Run() call and cannot be dropped via DropCapability. They are removed on return to prevent cross-channel bleed.

func (*Loop) SetContextProvider

func (l *Loop) SetContextProvider(provider ContextProvider)

SetContextProvider configures a provider for dynamic system prompt context.

func (*Loop) SetDebugConfig added in v0.6.0

func (l *Loop) SetDebugConfig(cfg config.DebugConfig)

SetDebugConfig configures debug options for the agent loop.

func (*Loop) SetEgoFile added in v0.7.0

func (l *Loop) SetEgoFile(path string)

SetEgoFile sets the path to ego.md. When set, the file is read fresh on each turn and its content is injected into the system prompt.

func (*Loop) SetExtractor added in v0.5.0

func (l *Loop) SetExtractor(e *memory.Extractor)

SetExtractor configures the automatic fact extractor.

func (*Loop) SetFailoverHandler

func (l *Loop) SetFailoverHandler(handler FailoverHandler)

SetFailoverHandler configures a handler to be called before model failover.

func (*Loop) SetHAInject added in v0.8.0

func (l *Loop) SetHAInject(fetcher homeassistant.StateFetcher)

SetHAInject configures the HA entity state resolver for tag context documents. When set, <!-- ha-inject: ... --> directives in context files are resolved to live entity state on each turn.

func (*Loop) SetInjectFiles added in v0.7.0

func (l *Loop) SetInjectFiles(paths []string)

SetInjectFiles sets the file paths whose content is re-read and injected into the system prompt on every turn. Paths should already have tilde expansion applied. Missing or unreadable files are silently skipped at read time.

func (*Loop) SetOrchestratorTools added in v0.7.0

func (l *Loop) SetOrchestratorTools(names []string)

SetOrchestratorTools configures the restricted tool set for all iterations of the agent loop. When set, only the named tools are advertised on every LLM call, keeping the primary model in orchestrator mode and steering it toward delegation. If thane_delegate is not registered in the tool registry, gating is silently disabled to avoid leaving the agent without actionable tools.

func (*Loop) SetTimezone added in v0.5.0

func (l *Loop) SetTimezone(tz string)

SetTimezone configures the IANA timezone for the Current Conditions section of the system prompt (e.g., "America/Chicago").

func (*Loop) SetUsageRecorder added in v0.7.1

func (l *Loop) SetUsageRecorder(store *usage.Store, pricing map[string]config.PricingEntry)

SetUsageRecorder configures persistent token usage recording. When set, every LLM completion in the agent loop is persisted for cost attribution and analysis.

func (*Loop) ShutdownArchive added in v0.3.0

func (l *Loop) ShutdownArchive(conversationID string)

ShutdownArchive archives the current conversation state before shutdown.

func (*Loop) SplitSession added in v0.7.0

func (l *Loop) SplitSession(conversationID string, atIndex int, atMessage string) error

SplitSession retroactively splits the current session at a past message boundary. Messages before the split point are archived as a completed session; messages at and after the split point are retained as the current session. Exactly one of atIndex (negative offset from end) or atMessage (substring match) must be non-zero.

func (*Loop) Tools

func (l *Loop) Tools() *tools.Registry

Tools returns the tool registry for adding additional tools.

func (*Loop) ToolsJSON

func (l *Loop) ToolsJSON() string

ToolsJSON returns the tools definition as JSON (for debugging).

func (*Loop) TriggerCompaction

func (l *Loop) TriggerCompaction(ctx context.Context, conversationID string) error

TriggerCompaction manually triggers conversation compaction.

type MemoryStore

type MemoryStore interface {
	GetMessages(conversationID string) []memory.Message
	AddMessage(conversationID, role, content string) error
	GetTokenCount(conversationID string) int
	Clear(conversationID string) error
	Stats() map[string]any
}

MemoryStore is the interface for memory storage.

type Message

type Message struct {
	Role    string `json:"role"` // system, user, assistant
	Content string `json:"content"`
}

Message represents a chat message.

type RelatedContact added in v0.8.0

type RelatedContact struct {
	Name string `json:"name"`
	Type string `json:"type,omitempty"`
}

RelatedContact represents a RELATED vCard entry on a contact.

type Request

type Request struct {
	Messages       []Message         `json:"messages"`
	Model          string            `json:"model,omitempty"`
	ConversationID string            `json:"conversation_id,omitempty"`
	Hints          map[string]string `json:"hints,omitempty"` // Routing hints (channel, mission, etc.)
	SkipContext    bool              `json:"-"`               // Skip memory, tools, and context injection (for lightweight completions)
	ExcludeTools   []string          `json:"-"`               // Tool names to exclude from this run (e.g., lifecycle tools for recurring wakes)
	SkipTagFilter  bool              `json:"-"`               // Bypass capability tag filtering (for self-scoping contexts like metacognitive)
}

Request represents an incoming agent request.

type Response

type Response struct {
	Content      string         `json:"content"`
	Model        string         `json:"model"`
	FinishReason string         `json:"finish_reason"`
	InputTokens  int            `json:"input_tokens,omitempty"`
	OutputTokens int            `json:"output_tokens,omitempty"`
	ToolsUsed    map[string]int `json:"tools_used,omitempty"` // tool name → call count
}

Response represents the agent's response.

type SessionArchiver added in v0.3.0

type SessionArchiver interface {
	// ArchiveConversation archives all messages from a conversation before clearing.
	ArchiveConversation(conversationID string, messages []memory.Message, reason string) error
	// StartSession begins a new session for a conversation.
	StartSession(conversationID string) (sessionID string, err error)
	// EndSession ends the current session.
	EndSession(sessionID string, reason string) error
	// ActiveSessionID returns the current session ID, or empty if none.
	ActiveSessionID(conversationID string) string
	// EnsureSession starts a session if none is active, returns the session ID.
	EnsureSession(conversationID string) string
	// ArchiveIterations copies iteration records to the immutable archive.
	ArchiveIterations(iterations []memory.ArchivedIteration) error
	// LinkPendingIterationToolCalls links archived tool calls to their
	// parent iterations using stored tool_call_ids.
	LinkPendingIterationToolCalls(sessionID string) error
	// OnMessage is called after each message to track session stats.
	OnMessage(conversationID string)
	// ActiveSessionStartedAt returns when the active session began,
	// or the zero time if there is no active session.
	ActiveSessionStartedAt(conversationID string) time.Time
}

SessionArchiver handles session lifecycle and message archiving.

type StreamCallback

type StreamCallback = llm.StreamCallback

StreamCallback receives streaming events. Alias to llm.StreamCallback for compatibility.

type StreamEvent

type StreamEvent = llm.StreamEvent

StreamEvent is a single event in a streaming response. Alias to llm.StreamEvent for use by consumers.

type ToolCallRecorder

type ToolCallRecorder interface {
	RecordToolCall(conversationID, messageID, toolCallID, toolName, arguments string) error
	CompleteToolCall(toolCallID, result, errMsg string) error
}

ToolCallRecorder optionally records tool call execution. Implemented by stores that support tool call tracking.

type TrustPolicyView added in v0.8.0

type TrustPolicyView struct {
	FrontierModel     bool   `json:"frontier_model"`
	ProactiveOutreach string `json:"proactive_outreach"`
	ToolAccess        string `json:"tool_access"`
	SendGating        string `json:"send_gating"`
}

TrustPolicyView is the JSON-serializable view of a trust zone's capability matrix. It exposes the policy dimensions that the agent needs to adapt its behavior.

Jump to

Keyboard shortcuts

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