session

package
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package session manages the agent session lifecycle including message history, token tracking, and context compaction.

Index

Constants

View Source
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
)
View Source
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
)
View Source
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.

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

View Source
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).

View Source
const (
	// DoomLoopThreshold is the number of identical tool calls before triggering detection.
	DoomLoopThreshold = 3
)

Variables

View Source
var ErrBusy = errors.New("session is busy")

BusyError is returned when the session is already processing.

Functions

func AskDoomLoopPermission

func AskDoomLoopPermission(ctx context.Context, sessionID, toolName string) error

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

func EstimateMessagesTokens(msgs []Message) int

EstimateMessagesTokens returns a rough token estimate across a slice of messages. Used to compute post-compaction context size for UI display.

func EstimateTokens

func EstimateTokens(s string) int

EstimateTokens estimates the token count for a string using chars/4. This matches opencode's Token.estimate() function.

func MessageToGoAI

func MessageToGoAI(msg Message) []goai.Message

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

func MessagesToGoAI(msgs []Message) []goai.Message

MessagesToGoAI converts session messages to goai format. This is the standalone version of Session.ToGoAIMessages().

func RetryDelay

func RetryDelay(attempt int, resp *http.Response) time.Duration

RetryDelay calculates the delay before the next retry attempt. Respects Retry-After headers if provided.

func RetryableError

func RetryableError(err error) string

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.

func (MemoryStore) Append

func (MemoryStore) Compact

func (MemoryStore) Compact(context.Context, []Message, int) error

func (MemoryStore) Load

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

func FromGoAIMessage(m goai.Message) Message

FromGoAIMessage converts a single goai message to a session message.

func FromGoAIMessages

func FromGoAIMessages(msgs []goai.Message) []Message

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

func (s *Session) AddMessage(msg Message)

AddMessage adds a message to the session history.

func (*Session) Cancel

func (s *Session) Cancel()

Cancel cancels the session.

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

func (s *Session) Context() context.Context

Context returns the session context.

func (*Session) FilterCompacted

func (s *Session) FilterCompacted() []Message

FilterCompacted returns messages up to and including the last compaction point. This prevents showing compacted context twice when rebuilding messages.

func (*Session) GetMessages

func (s *Session) GetMessages() []Message

GetMessages returns all messages in the session.

func (*Session) IsOverflow

func (s *Session) IsOverflow() bool

IsOverflow checks if the session has exceeded its context limit.

func (*Session) Prune

func (s *Session) Prune() int

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

func (s *Session) ToGoAIMessages() []goai.Message

ToGoAIMessages converts session messages to goai.Message format.

func (*Session) UpdateTokens

func (s *Session) UpdateTokens(usage stream.Usage)

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.

Jump to

Keyboard shortcuts

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