Documentation
¶
Overview ¶
Package session manages the agent session lifecycle including message history, token tracking, and context compaction.
Index ¶
- Constants
- Variables
- func AskDoomLoopPermission(ctx context.Context, sessionID, toolName string) error
- func DefaultPrunedMessage(info PrunedInfo) string
- func EstimateMessagesTokens(msgs []Message) int
- func EstimateTokens(s string) int
- func MessageToGoAI(msg Message) []goai.Message
- func MessagesToGoAI(msgs []Message) []goai.Message
- func RetryDelay(attempt int, resp *http.Response) time.Duration
- func RetryableError(err error) string
- type CompactOptions
- type CompactionAgent
- type CompactionConfig
- type DoomLoopDetector
- type FilePart
- type ImagePart
- type MemoryStore
- type Message
- type ModelLimits
- type Part
- type PrunedInfo
- type RetryStatus
- type Session
- func (s *Session) AddMessage(msg Message)
- func (s *Session) Cancel()
- func (s *Session) Compact(ctx context.Context, model stream.Model, conversationMessages []goai.Message, ...) (string, error)
- func (s *Session) CompactAndContinue(ctx context.Context, model stream.Model, conversationMessages []goai.Message, ...) error
- func (s *Session) Context() context.Context
- func (s *Session) FilterCompacted() []Message
- func (s *Session) GetMessages() []Message
- func (s *Session) IsOverflow() bool
- func (s *Session) Prune() int
- func (s *Session) PublishUpdate()
- func (s *Session) ToGoAIMessages() []goai.Message
- func (s *Session) UpdateTokens(usage stream.Usage)
- type SessionOptions
- type SessionStore
- type SimpleCompactionAgent
- type Tokens
- type ToolCallRecord
- type ToolPart
Constants ¶
const ( // OutputTokenMax is the maximum output tokens we expect from the model. // Matches opencode's SessionPrompt.OUTPUT_TOKEN_MAX. OutputTokenMax = 32000 // PruneMinimum is the minimum tokens to prune (to avoid tiny prunes). PruneMinimum = 20_000 // PruneProtect is the number of tokens worth of tool calls to protect. PruneProtect = 40_000 // CharsPerToken is the estimated number of characters per token. // Used for estimating token counts when actual counts aren't available. CharsPerToken = 4 // ImageTokenEstimate is the per-image token budget used during pruning. // Mid-of-road: OpenAI charges ~85-170 per tile, Anthropic ~1000/image. // Providers bill real image tokens regardless of whether we ship base64 // or a URL — so we can't count the transport string. ImageTokenEstimate = 1500 // FileTokenEstimate is the per-file token budget used during pruning. // Heuristic; non-image files are typically PDFs where token cost tracks // page count more than bytes. FileTokenEstimate = 1000 )
const ( // MaxRetryAttempts is the maximum number of retry attempts. MaxRetryAttempts = 5 // BaseRetryDelay is the base delay for exponential backoff. BaseRetryDelay = 2 * time.Second // MaxRetryDelay is the maximum delay between retries. MaxRetryDelay = 30 * time.Second )
const CompactionPrompt = `` /* 309-byte string literal not displayed */
CompactionPrompt is the default prompt for context compaction. This is appended as a user message to ask for a summary.
const CompactionTriggerPrompt = "What did we do so far?"
CompactionTriggerPrompt is added by opencode when a compaction part is converted to model messages. This matches MessageV2.toModelMessages() behavior for compaction parts.
const DefaultCompactionSystemPrompt = "" /* 603-byte string literal not displayed */
DefaultCompactionSystemPrompt is the system prompt used for the compaction agent. This matches opencode's agent/prompt/compaction.txt exactly (including trailing space on line 3 and trailing newline).
const (
// DoomLoopThreshold is the number of identical tool calls before triggering detection.
DoomLoopThreshold = 3
)
Variables ¶
var ErrBusy = errors.New("session is busy")
BusyError is returned when the session is already processing.
Functions ¶
func AskDoomLoopPermission ¶
AskDoomLoopPermission asks the user if they want to continue despite the doom loop. Returns nil if user approves, error if rejected. Uses the context-scoped permission manager if available, otherwise the global.
func DefaultPrunedMessage ¶
func DefaultPrunedMessage(info PrunedInfo) string
DefaultPrunedMessage returns a generic replacement string for pruned content.
func EstimateMessagesTokens ¶
EstimateMessagesTokens returns a rough token estimate across a slice of messages. Used to compute post-compaction context size for UI display.
func EstimateTokens ¶
EstimateTokens estimates the token count for a string using chars/4. This matches opencode's Token.estimate() function.
func MessageToGoAI ¶
MessageToGoAI converts a single session message to one or more goai messages. Tool-role messages may produce multiple goai messages (one per tool result).
func MessagesToGoAI ¶
MessagesToGoAI converts session messages to goai format. This is the standalone version of Session.ToGoAIMessages().
func RetryDelay ¶
RetryDelay calculates the delay before the next retry attempt. Respects Retry-After headers if provided.
func RetryableError ¶
RetryableError checks if an error is retryable and returns a reason. Returns empty string if not retryable.
Types ¶
type CompactOptions ¶
type CompactOptions struct {
// ProviderOptions are provider-specific options (store, promptCacheKey, etc.)
ProviderOptions map[string]any
// MaxOutputTokens limits the compaction response length.
MaxOutputTokens int
}
CompactOptions contains options for compaction requests. This matches opencode's behavior of passing the same provider options to compaction.
type CompactionAgent ¶
type CompactionAgent interface {
// SystemPrompt returns the system prompt for the compaction agent.
SystemPrompt() string
// Model returns the model ID to use for compaction, or empty to use session's model.
Model() string
}
CompactionAgent defines the interface for a compaction agent.
type CompactionConfig ¶
type CompactionConfig struct {
// Auto enables automatic compaction when context overflows. Default: true.
Auto bool
// Prune enables pruning of old tool outputs. Default: true.
Prune bool
// Agent is the agent to use for compaction. If nil, uses the model directly.
// When set, the agent's system prompt is used for the compaction request.
Agent CompactionAgent
// PrunedMessage generates replacement text when content is pruned or excluded.
// Called with metadata about the removed content. If nil, a default message is used.
PrunedMessage func(info PrunedInfo) string
}
CompactionConfig controls compaction behavior.
func DefaultCompactionConfig ¶
func DefaultCompactionConfig() CompactionConfig
DefaultCompactionConfig returns the default compaction configuration.
type DoomLoopDetector ¶
type DoomLoopDetector struct {
// contains filtered or unexported fields
}
DoomLoopDetector tracks recent tool calls to detect repetitive patterns.
func NewDoomLoopDetector ¶
func NewDoomLoopDetector() *DoomLoopDetector
NewDoomLoopDetector creates a new doom loop detector.
func (*DoomLoopDetector) LastToolName ¶
func (d *DoomLoopDetector) LastToolName() string
LastToolName returns the name of the last recorded tool.
func (*DoomLoopDetector) RecordCall ¶
func (d *DoomLoopDetector) RecordCall(name string, input json.RawMessage) bool
RecordCall records a tool call and returns true if a doom loop is detected.
func (*DoomLoopDetector) Reset ¶
func (d *DoomLoopDetector) Reset()
Reset clears the recorded calls (call after user confirms to continue).
type FilePart ¶
type FilePart struct {
Data string `json:"data"` // base64-encoded content
MimeType string `json:"mimeType"` // e.g., "application/pdf"
Filename string `json:"filename,omitempty"` // optional filename
// Source identifies where this file came from (e.g. a file key, URL, or ID).
// Session-level metadata only — not passed to goai or LLM providers.
Source string `json:"source,omitempty"`
}
FilePart represents a file attachment (base64-encoded).
type ImagePart ¶
type ImagePart struct {
Image string `json:"image"` // base64 data or URL
MimeType string `json:"mimeType,omitempty"` // e.g., "image/png"
// Source identifies where this image came from (e.g. a file key, URL, or ID).
// Session-level metadata only — not passed to goai or LLM providers.
Source string `json:"source,omitempty"`
}
ImagePart represents an image attachment (base64 or URL).
type MemoryStore ¶
type MemoryStore struct{}
MemoryStore is a no-op store for CLI mode and testing. Messages live only in the session's Messages slice; nothing is persisted.
type Message ¶
type Message struct {
ID string `json:"id"`
Role string `json:"role"` // "system", "user", "assistant", "tool"
Content string `json:"content,omitempty"`
Parts []Part `json:"parts,omitempty"`
ParentID string `json:"parentId,omitempty"` // For assistant messages, links to user message
Summary bool `json:"summary,omitempty"` // True if this is a compaction summary
Tokens Tokens `json:"tokens,omitempty"` // Token usage for this message
Compacted bool `json:"compacted,omitempty"` // True if tool outputs have been pruned
}
Message represents a message in the session history.
func FromGoAIMessage ¶
FromGoAIMessage converts a single goai message to a session message.
func FromGoAIMessages ¶
FromGoAIMessages converts goai messages to session messages.
type ModelLimits ¶
type ModelLimits struct {
Context int // Total context window size
Input int // Max input tokens (if different from context - output)
Output int // Max output tokens
}
ModelLimits defines the model's context window limits.
type Part ¶
type Part struct {
ID string `json:"id"`
Type string `json:"type"` // "text", "tool", "reasoning", "compaction", "image", "file"
Text string `json:"text,omitempty"`
Tool *ToolPart `json:"tool,omitempty"`
Image *ImagePart `json:"image,omitempty"`
File *FilePart `json:"file,omitempty"`
Compacted bool `json:"compacted,omitempty"` // True if output has been pruned
}
Part represents a part of a message (text, tool call, image, file, etc.)
type PrunedInfo ¶
type PrunedInfo struct {
Type string // "tool_output", "image", "file"
MimeType string // for images/files
Filename string // FilePart.Filename
Source string // ImagePart.Source (e.g. S3 key)
}
PrunedInfo describes content that was pruned or excluded from context.
type RetryStatus ¶
type RetryStatus struct {
Type string // "idle", "retry", "busy"
Attempt int // Current attempt number
Message string // Error message
Next time.Time // Time of next retry
}
RetryStatus tracks retry state for a session.
func NewRetryStatus ¶
func NewRetryStatus() RetryStatus
NewRetryStatus creates a new idle retry status.
func (*RetryStatus) IsBusy ¶
func (rs *RetryStatus) IsBusy() bool
IsBusy returns true if currently busy.
func (*RetryStatus) IsRetrying ¶
func (rs *RetryStatus) IsRetrying() bool
IsRetrying returns true if currently in retry state.
func (*RetryStatus) SetBusy ¶
func (rs *RetryStatus) SetBusy()
SetBusy updates status to busy state.
func (*RetryStatus) SetIdle ¶
func (rs *RetryStatus) SetIdle()
SetIdle updates status to idle state.
func (*RetryStatus) SetRetrying ¶
func (rs *RetryStatus) SetRetrying(attempt int, message string, next time.Time)
SetRetrying updates status to retrying state.
type Session ¶
type Session struct {
ID string
AgentName string
ModelID string
// Message history
Messages []Message
// Token tracking
Tokens Tokens
// Model limits for overflow detection
ModelLimits ModelLimits
// Compaction configuration
CompactionConfig CompactionConfig
// contains filtered or unexported fields
}
Session represents an active agent session.
func New ¶
func New(id, agentName, modelID string, limits ModelLimits) *Session
New creates a new session.
func NewWithOptions ¶
func NewWithOptions(opts SessionOptions) *Session
NewWithOptions creates a new session with options.
func (*Session) AddMessage ¶
AddMessage adds a message to the session history.
func (*Session) Compact ¶
func (s *Session) Compact(ctx context.Context, model stream.Model, conversationMessages []goai.Message, opts *CompactOptions) (string, error)
Compact performs context compaction by asking the model to summarize. conversationMessages should be the current conversation history (from the runner). Returns the summary text that should be used to continue the conversation.
func (*Session) CompactAndContinue ¶
func (s *Session) CompactAndContinue(ctx context.Context, model stream.Model, conversationMessages []goai.Message, opts *CompactOptions) error
CompactAndContinue performs compaction and adds a synthetic continue message. This is used for automatic compaction during the thinking loop. conversationMessages should be the current conversation history (from the runner).
func (*Session) FilterCompacted ¶
FilterCompacted returns messages up to and including the last compaction point. This prevents showing compacted context twice when rebuilding messages.
func (*Session) GetMessages ¶
GetMessages returns all messages in the session.
func (*Session) IsOverflow ¶
IsOverflow checks if the session has exceeded its context limit.
func (*Session) Prune ¶
Prune removes old tool outputs to reduce context size. Goes backwards through messages, keeping recent tool outputs and clearing older ones past the PruneProtect threshold.
func (*Session) PublishUpdate ¶
func (s *Session) PublishUpdate()
PublishUpdate publishes a session update event on the session's bus.
func (*Session) ToGoAIMessages ¶
ToGoAIMessages converts session messages to goai.Message format.
func (*Session) UpdateTokens ¶
UpdateTokens updates the session token counts from a step result. Note: We replace (not accumulate) because each API response's input tokens already includes ALL messages in the conversation, not just the new ones.
type SessionOptions ¶
type SessionOptions struct {
ID string
AgentName string
ModelID string
Limits ModelLimits
CompactionConfig *CompactionConfig // nil = use defaults
Bus *bus.Bus
}
SessionOptions contains options for creating a new session.
type SessionStore ¶
type SessionStore interface {
// Load returns the messages that should form the LLM context for the next
// turn. Implementations may hide pre-checkpoint history here — Sol only
// sees what the store decides to return. Returns empty slice (not error)
// if the conversation has no post-checkpoint messages yet.
Load(ctx context.Context) ([]Message, error)
// Append persists new messages from the current turn.
// Called after each step with the messages produced during that step.
Append(ctx context.Context, msgs []Message) error
// Compact atomically records a compaction: the summary messages become
// the head of the new context window, and the store advances its
// checkpoint so that subsequent Load calls return only `summary` + any
// later Appends. Pre-checkpoint messages are not deleted — the store
// decides how to keep them available for UI / audit.
// tokensFreed is the delta between pre- and post-compaction input tokens,
// recorded so the UI can show how much context was freed.
Compact(ctx context.Context, summary []Message, tokensFreed int) error
}
SessionStore provides pluggable persistence for conversation messages. Each store instance is pre-scoped to a single conversation by the caller. Sol doesn't know about conversation IDs, tenants, or routing.
type SimpleCompactionAgent ¶
type SimpleCompactionAgent struct {
// contains filtered or unexported fields
}
SimpleCompactionAgent is a simple implementation of CompactionAgent.
func NewCompactionAgent ¶
func NewCompactionAgent(systemPrompt, model string) *SimpleCompactionAgent
NewCompactionAgent creates a new compaction agent with the given system prompt and model.
func (*SimpleCompactionAgent) Model ¶
func (a *SimpleCompactionAgent) Model() string
Model returns the model ID to use for compaction.
func (*SimpleCompactionAgent) SystemPrompt ¶
func (a *SimpleCompactionAgent) SystemPrompt() string
SystemPrompt returns the system prompt for the compaction agent.
type Tokens ¶
type Tokens struct {
Input int `json:"input"`
Output int `json:"output"`
Reasoning int `json:"reasoning"`
Cache struct {
Read int `json:"read"`
Write int `json:"write"`
} `json:"cache"`
}
Tokens tracks token usage across the session.
type ToolCallRecord ¶
type ToolCallRecord struct {
Name string `json:"name"`
Input string `json:"input"` // JSON string for comparison
}
ToolCallRecord represents a tool call for doom loop detection.
type ToolPart ¶
type ToolPart struct {
CallID string `json:"callId"`
Name string `json:"name"`
Input string `json:"input,omitempty"`
Output string `json:"output,omitempty"`
Status string `json:"status"` // "pending", "running", "completed", "error"
Compacted bool `json:"compacted,omitempty"` // True if output has been pruned
}
ToolPart represents a tool call and its result.