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

baseAgent

package module
v0.5.1 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	MessageRoleCustom            = "custom"
	MessageRoleBranchSummary     = "branchSummary"
	MessageRoleCompactionSummary = "compactionSummary"
	BranchSummaryPrefix          = `The following is a summary of a branch that this conversation came back from:

<summary>
`
	BranchSummarySuffix = `</summary>`

	CompactionSummaryPrefix = `The conversation history before this point was compacted into the following summary:

<summary>
`
	CompactionSummarySuffix = `
</summary>`
)
View Source
const (
	EntryTypeMessage             = "message"
	EntryTypeThinkingLevelChange = "thinking_level_change"
	EntryTypeModelChange         = "model_change"
	EntryTypeCompaction          = "compaction"
	EntryTypeBranchSummary       = "branch_summary"
	EntryTypeCustom              = "custom"
	EntryTypeCustomMessage       = "custom_message"
	EntryTypeLabel               = "label"
	EntryTypeSessionInfo         = "session_info"
)

Session entry types, as stored in SessionEntryBase.Type.

View Source
const CurrentSessionVersion = 1
View Source
const DefaultThinkingLevel = ai.ThinkingLevelMedium
View Source
const SUMMARIZATION_PROMPT = `` /* 879-byte string literal not displayed */
View Source
const SUMMARIZATION_SYSTEM_PROMPT = `` /* 310-byte string literal not displayed */
View Source
const TURN_PREFIX_SUMMARIZATION_PROMPT = `` /* 440-byte string literal not displayed */
View Source
const UPDATE_SUMMARIZATION_PROMPT = `` /* 1257-byte string literal not displayed */

Variables

View Source
var (
	// ErrSessionNotFound is returned when a session ID has no stored session.
	ErrSessionNotFound = errors.New("session not found")
	// ErrSessionExists is returned by CreateSession when the header ID is taken.
	ErrSessionExists = errors.New("session already exists")
	// ErrSessionConflict is returned when another writer moved the session's
	// current position. Reload the session before writing again.
	ErrSessionConflict = errors.New("session changed by another writer")
)
View Source
var DEFAULT_COMPACTION_SETTINGS = CompactionSettings{
	Enabled:          true,
	ReservedTokens:   16384,
	KeepRecentTokens: 20000,
}
View Source
var ErrCompactionCancelled = errors.New("compaction cancelled")

ErrCompactionCancelled is returned by a CompactionHook to cancel compaction. It is reported as an aborted compaction; automatic compaction lets the prompt continue.

View Source
var ErrNothingToCompact = errors.New("nothing to compact")

ErrNothingToCompact is returned when everything since the last compaction fits in KeepRecentTokens, so compacting would not shrink the context. Threshold compaction treats it as a no-op rather than a failure.

Functions

func CalculateContextTokens added in v0.3.0

func CalculateContextTokens(usage ai.Usage) int64

*

  • Calculate total context tokens from usage.
  • Uses the native totalTokens field when available, falls back to computing from components.

func ConvertToLLM

func ConvertToLLM(messages []agent.AgentMessage) []ai.Message

func DecodeCompactionDetails added in v0.3.0

func DecodeCompactionDetails[T any](raw json.RawMessage) (details T, ok bool, err error)

DecodeCompactionDetails decodes details stored by a previous compaction. ok is false when the previous compaction stored no details.

func EstimateContextTokens added in v0.3.0

func EstimateContextTokens(messages []agent.AgentMessage) contextUsageEstimate

*

  • Estimate context tokens from messages, using the last assistant usage when available.
  • If there are messages after the last usage, estimate their tokens with estimateTokens.

func EstimateTokens added in v0.3.0

func EstimateTokens(message agent.AgentMessage) int64

func FindInitialModel

func FindInitialModel(registry *ModelRegistry) (ai.Model[ai.API], bool)

func FindTurnStartIndex added in v0.3.0

func FindTurnStartIndex(entries []SessionEntry, entryIndex, startIndex int) int

*

  • Find the user message that starts the turn containing the given entry index.
  • Returns -1 if no turn start found before the index.

func GenerateSummary added in v0.3.0

func GenerateSummary(
	ctx context.Context,
	currentMessages []agent.AgentMessage,
	model ai.Model[ai.API],
	reserveTokens int64,
	apiKey string,
	options *GenerateSummaryOptions,
) (string, error)

*

  • Generate a summary of the conversation using the LLM.
  • If previousSummary is provided, uses the update prompt to merge.

func GetLastAssistantUsage added in v0.3.0

func GetLastAssistantUsage(entries []SessionEntry) *ai.Usage

*

  • Find the last non-aborted assistant message usage from session entries.

func MarshalSessionEntry added in v0.3.0

func MarshalSessionEntry(entry SessionEntry) ([]byte, error)

MarshalSessionEntry encodes an entry for storage. It rejects entries that UnmarshalSessionEntry could not decode back into the same type.

func RegisterMessageDecoder added in v0.3.0

func RegisterMessageDecoder(role string, decoder MessageDecoder)

RegisterMessageDecoder lets UnmarshalSessionEntry decode messages of a custom role, matched against the message's "role" field. Call it during initialization. It panics if role is empty, built in, or already registered.

func ShouldTriggerCompaction added in v0.3.0

func ShouldTriggerCompaction(contextTokens int64, contextWindow int64, settings CompactionSettings) bool

*

  • Check if compaction should trigger based on context usage.
  • An unknown (non-positive) context window never triggers.

func UnmarshalAgentMessage added in v0.3.0

func UnmarshalAgentMessage(data []byte) (agent.AgentMessage, error)

UnmarshalAgentMessage decodes a built-in or registered agent message.

func UserMessageText added in v0.3.0

func UserMessageText(message ai.UserMessage) string

UserMessageText joins a user message's text blocks with spaces.

Types

type AgentSession

type AgentSession struct {
	// Use Prompt for session orchestration. Direct Agent runs bypass automatic
	// compaction; their persisted messages are checked on the next session prompt.
	Agent          *agent.Agent
	SessionManager *SessionManager
	// contains filtered or unexported fields
}

func NewAgentSession

func NewAgentSession(a *agent.Agent, sm *SessionManager) *AgentSession

func (*AgentSession) AbortCompaction added in v0.3.0

func (s *AgentSession) AbortCompaction()

func (*AgentSession) Compact added in v0.3.0

Compact manually compacts the active session branch and replaces the agent context.

An active run is aborted first. Messages queued for it with Steer or FollowUp are not sent: they are removed and reported in the compaction_end event's RestoredMessages. Prompts that arrive during compaction wait for it to finish and then run on the compacted context.

func (*AgentSession) GetActiveToolNames

func (s *AgentSession) GetActiveToolNames() ([]string, error)

* Get the names of currently active tools. * Returns the names of tools currently set on the agent.

func (*AgentSession) GetCompactionSettings added in v0.3.0

func (s *AgentSession) GetCompactionSettings() CompactionSettings

func (*AgentSession) GetFollowUpMode

func (s *AgentSession) GetFollowUpMode() (string, error)

* Current follow-up mode

func (*AgentSession) GetMessages

func (s *AgentSession) GetMessages() ([]agent.AgentMessage, error)

* Current messages in the conversation

func (*AgentSession) GetModel

func (s *AgentSession) GetModel() (ai.Model[ai.API], error)

* Current model (may be undefined if not yet selected)

func (*AgentSession) GetSessionID

func (s *AgentSession) GetSessionID() (string, error)

* Current session ID

func (*AgentSession) GetSessionName

func (s *AgentSession) GetSessionName() (*string, error)

* Current session name

func (*AgentSession) GetState

func (s *AgentSession) GetState() (agent.AgentState, error)

* Return full agent state

func (*AgentSession) GetSteeringMode

func (s *AgentSession) GetSteeringMode() (string, error)

* Current steering mode

func (*AgentSession) GetSystemPrompt

func (s *AgentSession) GetSystemPrompt() (string, error)

* Current effective system prompt

func (*AgentSession) GetThinkingLevel

func (s *AgentSession) GetThinkingLevel() (ai.ModelThinkingLevel, error)

* Current thinking level

func (*AgentSession) IsCompacting added in v0.3.0

func (s *AgentSession) IsCompacting() bool

func (*AgentSession) IsStreaming

func (s *AgentSession) IsStreaming() bool

* Whether agent is currently streaming a response

func (*AgentSession) Prompt

func (s *AgentSession) Prompt(ctx context.Context, text string, options *PromptOptions) error

*

  • Send a prompt to the agent.
  • - During streaming, queues via steer() or followUp() based on streamingBehavior option
  • - Validates model and API key before sending (when not streaming)
  • Error if streaming and no streamingBehavior specified
  • Error if no model selected or no API key available (when not streaming)

func (*AgentSession) SetCompactionHook added in v0.3.0

func (s *AgentSession) SetCompactionHook(hook CompactionHook)

SetCompactionHook replaces the compaction hook. Nil restores the default.

func (*AgentSession) SetCompactionSettings added in v0.3.0

func (s *AgentSession) SetCompactionSettings(settings CompactionSettings)

func (*AgentSession) Subscribe added in v0.3.0

func (s *AgentSession) Subscribe(listener AgentSessionEventListener) func()

Subscribe receives both forwarded agent events and session-specific events. The returned function removes this registration and is safe to call repeatedly. Changes to subscriptions during an emission apply to subsequent emissions.

type AgentSessionEvent added in v0.3.0

type AgentSessionEvent interface {
	agent.AgentEvent
}

AgentSessionEvent includes ordinary agent events and session-specific events. Consumers can use EventType or a type switch to distinguish them.

type AgentSessionEventListener added in v0.3.0

type AgentSessionEventListener func(event AgentSessionEvent)

AgentSessionEventListener receives synchronous session notifications. Listeners should return promptly and must not synchronously start or wait for session operations. They may subscribe, unsubscribe, inspect state, or cancel compaction. Queueing with Prompt's StreamingBehavior is also supported.

type AuthStore

type AuthStore interface {
	APIKey(provider ai.Provider) (string, bool)
}

AuthStore resolves provider API keys for model requests.

type BranchSummaryEntry

type BranchSummaryEntry struct {
	SessionEntryBase
	FromID   string `json:"fromId"`
	Summary  string `json:"summary"`
	Details  any    `json:"details,omitempty"`
	FromHook *bool  `json:"fromHook,omitempty"`
}

func (*BranchSummaryEntry) GetBase

func (e *BranchSummaryEntry) GetBase() *SessionEntryBase

type BranchSummaryMessage

type BranchSummaryMessage struct {
	Role      string `json:"role"`
	Summary   string `json:"summary"`
	FromID    string `json:"fromId"`
	Timestamp int64  `json:"timestamp"`
}

func CreateBranchSummaryMessage

func CreateBranchSummaryMessage(summary, fromID string, timestamp int64) BranchSummaryMessage

func (BranchSummaryMessage) AgentMessageType

func (BranchSummaryMessage) AgentMessageType() string

type CompactFunc added in v0.3.0

type CompactFunc func(ctx context.Context, req CompactionRequest) (*CompactionResult, error)

CompactFunc produces the compaction result for a request.

type CompactOptions added in v0.3.0

type CompactOptions struct {
	Headers            map[string]string
	CustomInstructions string
	ThinkingLevel      ai.ModelThinkingLevel
	Prompts            SummaryPrompts
}

CompactOptions contains the optional parameters for Compact.

type CompactionEndEvent added in v0.3.0

type CompactionEndEvent struct {
	Type    string            `json:"type"`
	Reason  CompactionReason  `json:"reason"`
	Result  *CompactionResult `json:"result,omitempty"`
	Aborted bool              `json:"aborted"`
	// WillRetry refers to overflow recovery, not queued-message continuation.
	WillRetry    bool   `json:"willRetry"`
	ErrorMessage string `json:"errorMessage,omitempty"`
	// RestoredMessages are steering and follow-up messages, steering first, that
	// were queued for the run a manual compaction aborted. They are removed from
	// the agent and will not be sent; resend them with Prompt to deliver them.
	RestoredMessages []agent.AgentMessage `json:"restoredMessages,omitempty"`
}

CompactionEndEvent is emitted after persistence and context replacement on success, before any retry starts. Result is nil when no compaction completed. Exhausted overflow recovery emits an end event without another start event.

func (CompactionEndEvent) EventType added in v0.3.0

func (CompactionEndEvent) EventType() string

type CompactionEntry

type CompactionEntry struct {
	SessionEntryBase
	Summary          string `json:"summary"`
	FirstKeptEntryID string `json:"firstKeptEntryId"`
	TokensBefore     int64  `json:"tokensBefore"`
	Details          any    `json:"details,omitempty"`
	FromHook         *bool  `json:"fromHook,omitempty"`
}

func GetLatestCompactionEntry

func GetLatestCompactionEntry(entries []SessionEntry) *CompactionEntry

func (*CompactionEntry) GetBase

func (e *CompactionEntry) GetBase() *SessionEntryBase

type CompactionHook added in v0.3.0

type CompactionHook func(ctx context.Context, req CompactionRequest, next CompactFunc) (*CompactionResult, error)

CompactionHook wraps manual and automatic compaction. It can call next for the default summary and adjust the result, return its own result, or return ErrCompactionCancelled to skip compaction.

The hook runs with session operations blocked and the agent idle. It must not call AgentSession methods other than AbortCompaction and IsCompacting, and must not write to the session; req.Session gives it read access.

type CompactionPreparation added in v0.3.0

type CompactionPreparation struct {
	FirstKeptEntryID    string
	MessagesToSummarize []agent.AgentMessage
	TurnPrefixMessages  []agent.AgentMessage
	IsSplitTurn         bool
	TokensBefore        int64
	PreviousSummary     *string
	// PreviousDetails is the previous compaction's Details as JSON, so it reads
	// the same before and after a reload. Decode it with DecodeCompactionDetails.
	PreviousDetails json.RawMessage
	Settings        CompactionSettings
}

func PrepareCompaction added in v0.3.0

func PrepareCompaction(
	pathEntries []SessionEntry,
	settings CompactionSettings,
) *CompactionPreparation

type CompactionReason added in v0.3.0

type CompactionReason string
const (
	CompactionReasonManual    CompactionReason = "manual"
	CompactionReasonThreshold CompactionReason = "threshold"
	CompactionReasonOverflow  CompactionReason = "overflow"
)

type CompactionRequest added in v0.3.0

type CompactionRequest struct {
	Reason        CompactionReason
	Preparation   *CompactionPreparation
	BranchEntries []SessionEntry
	// Session is the session being compacted, for reading only.
	Session            ReadonlySessionManager
	CustomInstructions string
	// Prompts starts as DefaultSummaryPrompts.
	Prompts SummaryPrompts
}

CompactionRequest describes one compaction of the active branch. Hooks may change a copy before passing it to next, but must not modify the entries.

type CompactionResult added in v0.3.0

type CompactionResult struct {
	Summary          string
	FirstKeptEntryID string
	TokensBefore     int64
	// Details is stored on the CompactionEntry and must marshal to JSON. The next
	// compaction receives it as CompactionPreparation.PreviousDetails.
	Details any
}

func Compact added in v0.3.0

func Compact(
	ctx context.Context,
	preparation *CompactionPreparation,
	model ai.Model[ai.API],
	apiKey string,
	options *CompactOptions,
) (*CompactionResult, error)

Compact generates summaries from prepared compaction data.

type CompactionSettings added in v0.3.0

type CompactionSettings struct {
	Enabled          bool
	ReservedTokens   int64
	KeepRecentTokens int64
}

type CompactionStartEvent added in v0.3.0

type CompactionStartEvent struct {
	Type   string           `json:"type"`
	Reason CompactionReason `json:"reason"`
}

func (CompactionStartEvent) EventType added in v0.3.0

func (CompactionStartEvent) EventType() string

type CompactionSummaryMessage

type CompactionSummaryMessage struct {
	Role         string `json:"role"`
	Summary      string `json:"summary"`
	TokensBefore int64  `json:"tokensBefore"`
	Timestamp    int64  `json:"timestamp"`
}

func CreateCompactionSummaryMessage

func CreateCompactionSummaryMessage(summary string, tokensBefore, timestamp int64) CompactionSummaryMessage

func (CompactionSummaryMessage) AgentMessageType

func (CompactionSummaryMessage) AgentMessageType() string

type CreateAgentSessionOptions

type CreateAgentSessionOptions struct {
	Model              ai.Model[ai.API]
	ThinkingLevel      ai.ModelThinkingLevel
	SessionManager     *SessionManager
	ModelRegistry      *ModelRegistry
	AuthStore          AuthStore
	CompactionSettings *CompactionSettings
	// CompactionHook wraps manual and automatic compaction. See CompactionHook.
	CompactionHook CompactionHook

	SystemPrompt      *string
	Tools             []agent.AgentTool[any, any]
	ToolExecutionMode *agent.ToolExecutionMode

	/* Optional func to call before each tool call is executed. */
	BeforeToolCall func(
		ctx context.Context,
		call agent.BeforeToolCallContext,
	) (*agent.BeforeToolCallResult, error)

	/* Optional func to call after each tool call is executed. */
	AfterToolCall func(
		ctx context.Context,
		call agent.AfterToolCallContext[any],
	) (*agent.AfterToolCallResult, error)

	/* Optional func to call before each payload is sent to the provider. */
	OnPayload func(
		payload any,
		model ai.Model[ai.API],
	) (any, error)

	/* Optional func to call after each response is received from the provider. */
	OnResponse func(
		response ai.ProviderResponse,
		model ai.Model[ai.API],
	) error
}

type CreateAgentSessionResult

type CreateAgentSessionResult struct {
	Session              *AgentSession
	ModelFallbackMessage *string
}

func CreateAgentSession

func CreateAgentSession(ctx context.Context, options CreateAgentSessionOptions) (*CreateAgentSessionResult, error)

CreateAgentSession creates a minimal, generic agent session with no tools.

type CustomEntry

type CustomEntry struct {
	SessionEntryBase
	CustomType string `json:"customType"`
	Data       any    `json:"data,omitempty"`
}

func (*CustomEntry) GetBase

func (e *CustomEntry) GetBase() *SessionEntryBase

type CustomMessage

type CustomMessage struct {
	Role       string           `json:"role"`
	CustomType string           `json:"customType"`
	Content    []ai.UserContent `json:"content"`
	Display    bool             `json:"display"`
	Details    any              `json:"details,omitempty"`
	Timestamp  int64            `json:"timestamp"`
}

func CreateCustomMessage

func CreateCustomMessage(customType string, content []ai.UserContent, display bool, details any, timestamp int64) CustomMessage

func (CustomMessage) AgentMessageType

func (CustomMessage) AgentMessageType() string

type CustomMessageEntry

type CustomMessageEntry struct {
	SessionEntryBase
	CustomType string           `json:"customType"`
	Content    []ai.UserContent `json:"content"`
	Details    any              `json:"details,omitempty"`
	Display    bool             `json:"display"`
}

func (*CustomMessageEntry) GetBase

func (e *CustomMessageEntry) GetBase() *SessionEntryBase

type CutPointResult added in v0.3.0

type CutPointResult struct {
	FirstKeptEntryIndex int
	TurnStartIndex      int
	IsSplitTurn         bool
}

func FindCutPoint added in v0.3.0

func FindCutPoint(entries []SessionEntry, startIndex, endIndex int, keepRecentTokens int64) CutPointResult

FindCutPoint finds the entry that keeps approximately keepRecentTokens.

type EnvAuthStore

type EnvAuthStore struct{}

EnvAuthStore resolves API keys from environment variables.

func (EnvAuthStore) APIKey

func (EnvAuthStore) APIKey(provider ai.Provider) (string, bool)

type GenerateSummaryOptions added in v0.3.0

type GenerateSummaryOptions struct {
	Headers            map[string]string
	CustomInstructions string
	PreviousSummary    string
	ThinkingLevel      ai.ModelThinkingLevel
	Prompts            SummaryPrompts
}

type LabelEntry

type LabelEntry struct {
	SessionEntryBase
	TargetID string  `json:"targetId"`
	Label    *string `json:"label,omitempty"`
}

func (*LabelEntry) GetBase

func (e *LabelEntry) GetBase() *SessionEntryBase

type LoadedSession added in v0.3.0

type LoadedSession struct {
	Header SessionHeader
	// Entries holds every branch, in append order.
	Entries []SessionEntry
	// CurrentEntryID is the current position: the entry the next append
	// attaches to. Nil with entries present means "before the first entry",
	// so the next append starts a new root.
	CurrentEntryID *string
}

LoadedSession is a stored session with every entry it has.

type ManualCompactionOptions added in v0.3.0

type ManualCompactionOptions struct {
	CustomInstructions string
}

type MessageDecoder added in v0.3.0

type MessageDecoder func(data []byte) (agent.AgentMessage, error)

MessageDecoder decodes the JSON of an agent message with a registered role.

type ModelChangeEntry

type ModelChangeEntry struct {
	SessionEntryBase
	Provider string `json:"provider"`
	ModelID  string `json:"modelId"`
}

func (*ModelChangeEntry) GetBase

func (e *ModelChangeEntry) GetBase() *SessionEntryBase

type ModelRegistry

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

ModelRegistry provides model lookup and request auth resolution.

func NewModelRegistry

func NewModelRegistry(auth AuthStore) *ModelRegistry

NewModelRegistry creates a registry using the built-in ai.Models list.

func NewModelRegistryWithModels

func NewModelRegistryWithModels(auth AuthStore, models map[ai.Provider]map[string]ai.Model[ai.API]) *ModelRegistry

NewModelRegistryWithModels creates a registry using an explicit model set.

func (*ModelRegistry) APIKeyAndHeaders

func (r *ModelRegistry) APIKeyAndHeaders(model ai.Model[ai.API]) (RequestAuth, error)

APIKeyAndHeaders resolves the API key and headers to use for a model request.

func (*ModelRegistry) APIKeyForProvider

func (r *ModelRegistry) APIKeyForProvider(provider ai.Provider) (string, bool)

APIKeyForProvider resolves a provider API key.

func (*ModelRegistry) All

func (r *ModelRegistry) All() []ai.Model[ai.API]

All returns every model in deterministic provider/id order.

func (*ModelRegistry) Available

func (r *ModelRegistry) Available() []ai.Model[ai.API]

Available returns models whose provider has configured auth.

func (*ModelRegistry) Find

func (r *ModelRegistry) Find(provider ai.Provider, modelID string) (ai.Model[ai.API], bool)

Find returns a model by provider and model ID.

func (*ModelRegistry) HasConfiguredAuth

func (r *ModelRegistry) HasConfiguredAuth(model ai.Model[ai.API]) bool

HasConfiguredAuth reports whether a model's provider has an API key.

func (*ModelRegistry) ProviderDisplayName

func (r *ModelRegistry) ProviderDisplayName(provider ai.Provider) string

ProviderDisplayName returns a human-friendly provider name.

type PromptOptions

type PromptOptions struct {
	Images []ai.ImageContent
	/** When streaming, how to queue the message: "steer" (interrupt) or "followUp" (wait). Required if streaming. */
	StreamingBehavior StreamingBehavior
}

type ReadonlySessionManager added in v0.3.0

type ReadonlySessionManager interface {
	GetSessionID() string
	GetHeader() *SessionHeader
	GetSessionName() *string
	GetLeafID() *string
	GetLeafEntry() SessionEntry
	GetEntry(id string) SessionEntry
	GetChildren(parentID string) []SessionEntry
	GetLabel(id string) *string
	GetBranch(from ...string) []SessionEntry
	GetBranches() []SessionBranch
	GetEntries() []SessionEntry
	GetTree() []*SessionTreeNode
	BuildSessionContext() SessionContext
}

ReadonlySessionManager is the read side of SessionManager, for code such as hooks that may inspect the session but must not write to it.

type RequestAuth

type RequestAuth struct {
	APIKey  *string
	Headers map[string]string
}

RequestAuth is the request-level auth and headers resolved for a model.

type SessionBranch added in v0.3.0

type SessionBranch struct {
	// LeafID is the branch's last entry. SetLeaf(LeafID) switches to it.
	LeafID string
	// BranchPointID is the nearest entry above the leaf with more than one
	// child, where this branch splits from its siblings. It is nil when the
	// branch splits from another root, or the session has a single branch.
	BranchPointID *string
	// FirstEntryID is the first entry after BranchPointID. The branch's own
	// entries run from it to LeafID.
	FirstEntryID string
	// Label is the label nearest the leaf on the branch's own entries.
	Label *string
	// FirstMessage is the text of the first user message on the branch's own
	// entries. If they have none, as when a reply was regenerated, it is the
	// last user message before them.
	FirstMessage string
	// Active reports whether the current position is on the branch's own
	// entries. At a branch point, before the next append, no branch is active.
	Active bool
}

SessionBranch is one branch of a session's entry tree: the path from the root to a leaf, an entry with no children.

type SessionContext

type SessionContext struct {
	Messages      []agent.AgentMessage
	ThinkingLevel string
	Model         *SessionModel
}

func BuildSessionContext

func BuildSessionContext(entries []SessionEntry, leafID *string, byID map[string]SessionEntry) SessionContext

type SessionEntry

type SessionEntry interface{ GetBase() *SessionEntryBase }

func UnmarshalSessionEntry added in v0.3.0

func UnmarshalSessionEntry(data []byte) (SessionEntry, error)

UnmarshalSessionEntry decodes an entry encoded by MarshalSessionEntry. Unknown entry types, message roles, and content types are errors, so a partially decoded entry never reaches the agent's context.

type SessionEntryBase

type SessionEntryBase struct {
	Type      string  `json:"type"`
	ID        string  `json:"id"`
	ParentID  *string `json:"parentId"`
	Timestamp string  `json:"timestamp"`
}

type SessionHeader

type SessionHeader struct {
	Type          string         `json:"type"`
	Version       int            `json:"version,omitempty"`
	ID            string         `json:"id"`
	Timestamp     string         `json:"timestamp"`
	ParentSession *string        `json:"parentSession,omitempty"`
	Name          *string        `json:"name,omitempty"`
	Metadata      map[string]any `json:"metadata,omitempty"`
}

func NewDefaultSessionHeader

func NewDefaultSessionHeader(parentSession *string, metadata map[string]any) *SessionHeader

NewDefaultSessionHeader returns a header for a new session with a fresh ID.

type SessionInfo

type SessionInfo struct {
	ID string
	// Name is the latest session_info entry's name, or nil if that entry
	// cleared it. Without any session_info entry a store may supply its own
	// name, such as one generated from the first message.
	Name            *string
	ParentSessionID *string
	// Created is the header timestamp.
	Created time.Time
	// Modified is when the store last appended an entry, or Created if it
	// has none.
	Modified time.Time
	// MessageCount counts message entries across every branch.
	MessageCount int
	// FirstMessage is the text of the first user message with any text, in
	// append order, or "" if there is none.
	FirstMessage string
	Metadata     map[string]any
}

SessionInfo summarizes a stored session for listing.

func BuildSessionInfo added in v0.3.0

func BuildSessionInfo(header SessionHeader, entries []SessionEntry, modified time.Time) SessionInfo

BuildSessionInfo derives SessionInfo from a session's header and entries in append order. modified is the store's time of the last append; pass the zero time when there are no entries. Stores that keep these values in columns should update them the same way on each append.

func FindMostRecentSession added in v0.3.0

func FindMostRecentSession(ctx context.Context, store SessionStore, metadata map[string]any) (*SessionInfo, error)

FindMostRecentSession returns the most recently modified session matching metadata, or nil if there is none.

type SessionInfoEntry

type SessionInfoEntry struct {
	SessionEntryBase
	Name *string `json:"name,omitempty"`
}

func (*SessionInfoEntry) GetBase

func (e *SessionInfoEntry) GetBase() *SessionEntryBase

type SessionManager

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

SessionManager holds a session's entry tree and current position, and saves changes through a SessionStore when persisted. It is safe for concurrent use.

Its methods only lock mu and delegate to state. Writes hold mu across their store call, so each entry's parent is the position the previous write left, and reads wait for an in-flight write rather than see the state before it.

func InMemorySessionManager

func InMemorySessionManager(ctx context.Context) (*SessionManager, error)

func NewSessionManager

func NewSessionManager(ctx context.Context, header *SessionHeader, sessionID *string, persist bool, store SessionStore, metadata map[string]any) (*SessionManager, error)

NewSessionManager loads the stored session sessionID, or creates a new session when sessionID is nil. A new session uses header, with any empty fields filled in: pass &SessionHeader{ID: id} for a custom session ID. metadata is used when header has none.

func (*SessionManager) AppendCompaction

func (s *SessionManager) AppendCompaction(ctx context.Context, summary, first string, tokens int64, details any, fromHook *bool) (string, error)

func (*SessionManager) AppendCustomEntry

func (s *SessionManager) AppendCustomEntry(ctx context.Context, t string, data any) (string, error)

func (*SessionManager) AppendCustomMessageEntry

func (s *SessionManager) AppendCustomMessageEntry(ctx context.Context, t string, c []ai.UserContent, display bool, details any) (string, error)

func (*SessionManager) AppendLabelChange

func (s *SessionManager) AppendLabelChange(ctx context.Context, target string, label *string) (string, error)

AppendLabelChange sets the label of an entry, or clears it when label is nil or empty.

func (*SessionManager) AppendMessage

func (s *SessionManager) AppendMessage(ctx context.Context, m agent.AgentMessage) (string, error)

func (*SessionManager) AppendMessageWithID added in v0.1.3

func (s *SessionManager) AppendMessageWithID(ctx context.Context, id string, m agent.AgentMessage) (string, error)

AppendMessageWithID appends a message with the given entry ID, or a fresh one when id is empty.

func (*SessionManager) AppendModelChange

func (s *SessionManager) AppendModelChange(ctx context.Context, p, m string) (string, error)

func (*SessionManager) AppendSessionInfo

func (s *SessionManager) AppendSessionInfo(ctx context.Context, name string) (string, error)

func (*SessionManager) AppendThinkingLevelChange

func (s *SessionManager) AppendThinkingLevelChange(ctx context.Context, v string) (string, error)

func (*SessionManager) BranchWithSummary added in v0.3.0

func (s *SessionManager) BranchWithSummary(ctx context.Context, from *string, summary string, details any, fromHook *bool) (string, error)

BranchWithSummary moves the current position to from, or before the first entry when from is nil, and appends a summary there of the branch being left.

func (*SessionManager) BuildSessionContext

func (s *SessionManager) BuildSessionContext() SessionContext

func (*SessionManager) Fork added in v0.3.0

func (s *SessionManager) Fork(ctx context.Context, leafID *string) (*SessionManager, error)

Fork creates a new session from the path from the root to leafID, which then continues independently of this one. The entries keep their IDs. Labels on the path are carried over as new label entries after it, keeping their timestamps. A nil leafID forks from the root, giving a session with no entries. The fork is saved through this manager's store when persisted.

func (*SessionManager) GetBranch

func (s *SessionManager) GetBranch(from ...string) []SessionEntry

GetBranch returns the path from the root to from, or to the current position when from is omitted.

func (*SessionManager) GetBranches added in v0.3.0

func (s *SessionManager) GetBranches() []SessionBranch

GetBranches returns every branch of the entry tree, in tree order: depth first, with children in append order.

func (*SessionManager) GetChildren

func (s *SessionManager) GetChildren(parentID string) []SessionEntry

GetChildren returns the entries whose parent is parentID, in append order.

func (*SessionManager) GetEntries

func (s *SessionManager) GetEntries() []SessionEntry

GetEntries returns every entry across all branches, in append order.

func (*SessionManager) GetEntry

func (s *SessionManager) GetEntry(id string) SessionEntry

func (*SessionManager) GetHeader

func (s *SessionManager) GetHeader() *SessionHeader

GetHeader returns a copy of the session header.

func (*SessionManager) GetLabel

func (s *SessionManager) GetLabel(id string) *string

func (*SessionManager) GetLeafEntry

func (s *SessionManager) GetLeafEntry() SessionEntry

func (*SessionManager) GetLeafID

func (s *SessionManager) GetLeafID() *string

func (*SessionManager) GetSessionID

func (s *SessionManager) GetSessionID() string

func (*SessionManager) GetSessionName

func (s *SessionManager) GetSessionName() *string

func (*SessionManager) GetTree

func (s *SessionManager) GetTree() []*SessionTreeNode

GetTree returns the entry tree, with children in append order.

func (*SessionManager) IsPersisted

func (s *SessionManager) IsPersisted() bool

func (*SessionManager) LoadSession

func (s *SessionManager) LoadSession(ctx context.Context, id string) error

LoadSession switches the manager to a stored session, resuming at its saved position. On error the manager is unchanged.

func (*SessionManager) NewSession

func (s *SessionManager) NewSession(ctx context.Context, header *SessionHeader, metadata map[string]any) error

NewSession switches the manager to a new, empty session. See NewSessionManager for header and metadata. On error the manager is unchanged.

func (*SessionManager) Reload added in v0.3.0

func (s *SessionManager) Reload(ctx context.Context) error

Reload replaces the manager's state with the stored session, such as after ErrSessionConflict reports that another writer changed it.

func (*SessionManager) ResetLeaf

func (s *SessionManager) ResetLeaf(ctx context.Context) error

ResetLeaf moves the current position before the first entry, so the next append starts a new root.

func (*SessionManager) SetLeaf

func (s *SessionManager) SetLeaf(ctx context.Context, id string) error

SetLeaf moves the current position to an existing entry, so the next append starts a branch from it. A persistent session saves the position first.

type SessionMessageEntry

type SessionMessageEntry struct {
	SessionEntryBase
	Message agent.AgentMessage `json:"message"`
}

func (*SessionMessageEntry) GetBase

func (e *SessionMessageEntry) GetBase() *SessionEntryBase

type SessionModel

type SessionModel struct{ Provider, ModelID string }

type SessionQuery added in v0.3.0

type SessionQuery struct {
	// Metadata matches sessions whose header metadata has every key with an
	// equal value. Nil or empty matches every session.
	Metadata map[string]any
	// Limit caps the number of sessions returned. Zero means no limit.
	Limit int
}

SessionQuery selects sessions for ListSessions.

type SessionStore

type SessionStore interface {
	CreateSession(ctx context.Context, header SessionHeader, entries []SessionEntry) error
	AppendEntry(ctx context.Context, sessionID string, entry SessionEntry) error
	SetCurrentEntry(ctx context.Context, sessionID string, expected, entryID *string) error
	LoadSession(ctx context.Context, sessionID string) (LoadedSession, error)
	ListSessions(ctx context.Context, query SessionQuery) ([]SessionInfo, error)
}

SessionStore persists sessions. baseAgent ships no implementation, so any database can back it. sessionstoretest.Run checks an implementation against the rules below, and MarshalSessionEntry and UnmarshalSessionEntry encode entries for storage.

Ordering and scope:

  • Entries come back in append order. Use an insertion sequence, not entry timestamps or IDs, which do not sort in append order.
  • LoadSession returns all of a session's entries across every branch. baseAgent works out the active path itself.
  • Entry IDs are unique per session, not globally: key entries by (session ID, entry ID). Forks keep their source entries' IDs.

Position and conflicts:

  • Every session has a current position, which AppendEntry and SetCurrentEntry move. It is how baseAgent detects another writer.
  • AppendEntry is safe to retry. Appending an entry whose ID the session already has succeeds without changing anything; check this before the conflict check, so a retry of a write that landed is not a conflict.
  • Otherwise AppendEntry returns ErrSessionConflict unless entry.ParentID equals the current position (both nil counts as equal). On success the entry becomes the current position.
  • SetCurrentEntry returns ErrSessionConflict unless expected equals the current position. Branching within a session has no method of its own: it is SetCurrentEntry followed by AppendEntry.
  • The conflict check is a safety net, not a scheduler. Deployments should keep one live AgentSession per session, through sticky routing or a lease.

Creating and forking:

  • CreateSession is all-or-nothing. It returns ErrSessionExists if the header ID is taken, and fails without storing anything if any entry cannot be stored, such as one with a duplicate ID. The current position is the last entry, or nil without entries.
  • A fork is CreateSession with header.ParentSession set and the copied entries, which keep their IDs. Its entries are empty for a fork from the root. A fork is independent of its source once created.

Session info: stores derive SessionInfo themselves, as BuildSessionInfo does. ListSessions sorts by Modified, newest first.

Methods given an unknown session ID return an error wrapping ErrSessionNotFound. Stores must not modify the headers and entries passed to them.

type SessionTreeNode

type SessionTreeNode struct {
	Entry          SessionEntry       `json:"entry"`
	Children       []*SessionTreeNode `json:"children"`
	Label          *string            `json:"label,omitempty"`
	LabelTimestamp *string            `json:"labelTimestamp,omitempty"`
}

type StreamingBehavior

type StreamingBehavior string
const (
	StreamingBehaviorSteer    StreamingBehavior = "steer"
	StreamingBehaviorFollowUp StreamingBehavior = "followUp"
)

type SummaryPrompts added in v0.3.0

type SummaryPrompts struct {
	System string
	// Initial asks for a new summary; Update merges into <previous-summary>.
	Initial    string
	Update     string
	TurnPrefix string
}

SummaryPrompts are the prompts used to generate compaction summaries. Empty fields use the defaults from DefaultSummaryPrompts.

func DefaultSummaryPrompts added in v0.3.0

func DefaultSummaryPrompts() SummaryPrompts

type ThinkingLevelChangeEntry

type ThinkingLevelChangeEntry struct {
	SessionEntryBase
	ThinkingLevel string `json:"thinkingLevel"`
}

func (*ThinkingLevelChangeEntry) GetBase

Directories

Path Synopsis
Package sessionstoretest checks baseAgent.SessionStore implementations against the contract documented on baseAgent.SessionStore.
Package sessionstoretest checks baseAgent.SessionStore implementations against the contract documented on baseAgent.SessionStore.

Jump to

Keyboard shortcuts

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