Documentation
¶
Overview ¶
Package session — writelock.go implements file-based session write locks (REQ-460).
Before writing to a session JSONL file, an exclusive lock is acquired using atomic file creation (O_CREATE|O_EXCL). The lock file contains a JSON payload with PID, creation time, and process start time for stale detection.
Stale lock detection:
- Dead PID (process not alive)
- Recycled PID (process start time mismatch)
- Age > maxLockAge (30 minutes)
A watchdog goroutine periodically reclaims over-held locks. Signal handlers release all locks on SIGINT/SIGTERM.
Index ¶
- Variables
- func EstimateTokens(msgs []Message) int
- func ToOpenAI(msgs []Message) []openai.ChatCompletionMessage
- type Entry
- type LockManager
- type LockPayload
- type Manager
- func (m *Manager) ActiveSessions() map[string]time.Time
- func (m *Manager) AppendMessages(key string, msgs []Message) error
- func (m *Manager) Close()
- func (m *Manager) FlushSave()
- func (m *Manager) GetHistory(key string) ([]Message, error)
- func (m *Manager) Reap(maxAge time.Duration) (int, error)
- func (m *Manager) ReplaceHistory(key string, msgs []Message) error
- func (m *Manager) Reset(key string)
- func (m *Manager) ResetAll()
- func (m *Manager) ResetIdle(maxIdle time.Duration) int
- func (m *Manager) SetPruning(p PruningPolicy)
- func (m *Manager) StartReapLoop(ctx context.Context, interval, maxAge time.Duration)
- func (m *Manager) StartResetLoop(ctx context.Context, dailyMode string, atHour int, idleMinutes int, ...)
- func (m *Manager) Stop()
- func (m *Manager) TouchAPICall(key string)
- func (m *Manager) TrimMessages(key string, maxMessages int) error
- type Message
- type PruningPolicy
- type WriteLock
Constants ¶
This section is empty.
Variables ¶
var ErrSessionNotFound = errors.New("session not found")
ErrSessionNotFound is returned when a session key does not match any active session.
Functions ¶
func EstimateTokens ¶
EstimateTokens exposes the token estimator for external callers (e.g. soft trim).
func ToOpenAI ¶
func ToOpenAI(msgs []Message) []openai.ChatCompletionMessage
ToOpenAI converts Messages to the openai.ChatCompletionMessage format. Orphaned tool result messages (ToolCallID not matching any preceding assistant's ToolCalls) are silently dropped as a safety net; the primary cleanup happens in GetHistory which persists the fix.
Types ¶
type Entry ¶
type Entry struct {
SessionID string `json:"sessionId"`
UpdatedAt int64 `json:"updatedAt"` // unix ms
}
Entry tracks metadata for a session.
type LockManager ¶
type LockManager struct {
// contains filtered or unexported fields
}
LockManager manages file-based write locks with watchdog and cleanup.
func NewLockManager ¶
func NewLockManager(logger *zap.SugaredLogger) *LockManager
NewLockManager creates a lock manager and starts the watchdog goroutine.
func (*LockManager) Acquire ¶
func (lm *LockManager) Acquire(sessionPath string) (*WriteLock, error)
Acquire attempts to acquire a write lock for a session JSONL file. Returns a WriteLock that must be Released when done.
func (*LockManager) Release ¶
func (lm *LockManager) Release(lock *WriteLock)
Release releases a specific lock and removes it from tracking.
func (*LockManager) ReleaseAll ¶
func (lm *LockManager) ReleaseAll()
ReleaseAll releases all currently held locks (used on signal shutdown).
func (*LockManager) Stop ¶
func (lm *LockManager) Stop()
Stop shuts down the watchdog and releases all held locks.
type LockPayload ¶
type LockPayload struct {
PID int `json:"pid"`
CreatedAt string `json:"createdAt"` // ISO8601
StartTime int64 `json:"starttime"` // process start time (jiffies or boot-relative)
}
LockPayload is stored in the lock file for stale detection.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles session persistence.
func New ¶
New creates a session Manager. dir is the sessions directory (e.g. ~/.roger/agents/main/sessions).
func (*Manager) ActiveSessions ¶
ActiveSessions returns a snapshot of all active session keys and their last update time.
func (*Manager) AppendMessages ¶
AppendMessages adds messages to a session's history.
func (*Manager) Close ¶ added in v1.2.0
func (m *Manager) Close()
Close stops background goroutines (lock manager watchdog).
func (*Manager) FlushSave ¶
func (m *Manager) FlushSave()
FlushSave forces an immediate write of sessions.json. Call on shutdown. Always writes regardless of dirty flag to ensure no data is lost.
func (*Manager) GetHistory ¶
GetHistory returns the message history for a session key. Returns nil if the session is new or expired.
func (*Manager) Reap ¶
Reap deletes JSONL files in the sessions directory that are older than maxAge and have no corresponding active session. Returns count of files removed.
func (*Manager) ReplaceHistory ¶
ReplaceHistory replaces the entire session JSONL with msgs. Returns nil if the session doesn't exist.
func (*Manager) ResetIdle ¶
ResetIdle resets sessions that have been idle longer than the given duration.
func (*Manager) SetPruning ¶
func (m *Manager) SetPruning(p PruningPolicy)
SetPruning configures token-based context pruning.
func (*Manager) StartReapLoop ¶
StartReapLoop runs Reap periodically in the background until ctx is cancelled.
func (*Manager) StartResetLoop ¶
func (m *Manager) StartResetLoop(ctx context.Context, dailyMode string, atHour int, idleMinutes int, logFn func(string, ...any))
StartResetLoop starts background goroutines for daily and idle resets.
func (*Manager) Stop ¶
func (m *Manager) Stop()
Stop shuts down the lock manager and flushes pending saves. Call on shutdown.
func (*Manager) TouchAPICall ¶
TouchAPICall records the current time as the last API call for a session. When CacheTTL > 0 in the pruning policy, pruning is deferred until the TTL after this timestamp expires, preserving the provider's prompt cache.
type Message ¶
type Message struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
ImageURLs []string `json:"image_urls,omitempty"` // base64 data URLs or HTTP URLs for vision
ToolCalls []openai.ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
TS int64 `json:"ts"`
}
Message is a single conversation entry stored in JSONL.
func FromOpenAI ¶
func FromOpenAI(msgs []openai.ChatCompletionMessage) []Message
FromOpenAI converts openai messages to session Messages.
func PruneToolResults ¶
PruneToolResults performs surgical pruning: compresses old tool-result messages to short placeholders while preserving user and assistant messages. The last keepRecent assistant+tool sequences are left untouched so the model has recent tool context available.
This keeps the conversation flow intact (user questions and assistant reasoning) while reclaiming the majority of tokens — tool results are typically 80%+ of a long session's token budget.
func SanitizeOrphans ¶
SanitizeOrphans removes tool result messages whose ToolCallID doesn't match any preceding assistant message's ToolCalls. Returns the cleaned slice and the number of messages removed.
type PruningPolicy ¶
type PruningPolicy struct {
HardClearRatio float64 // fraction of model max tokens to trigger clear (default 0.5)
ModelMaxTokens int // model context window size (default 16384)
KeepLastAssistants int // assistant messages to keep after hard clear (default 2)
SoftTrimRatio float64 // fraction of model max tokens to trigger soft trim (default 0.0 = disabled)
SurgicalPruning bool // when true, prefer trimming tool results before hard clear
CacheTTL time.Duration // prompt-cache lifetime to respect before pruning (default 0 = prune immediately)
}
PruningPolicy holds token-based context pruning parameters.