session

package
v0.260806.1 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MPL-2.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Timeout          time.Duration // Session timeout duration
	MessageRetention time.Duration // Message retention window
}

Config holds session manager configuration

type Manager

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

Manager handles session lifecycle

func NewManager

func NewManager(cfg Config, store SessionStore) *Manager

NewManager creates a new session manager

func (*Manager) AppendMessage

func (m *Manager) AppendMessage(id string, msg Message) bool

AppendMessage adds a message to a session's transcript.

The append goes straight to the store rather than through Update: the transcript is append-only and lives outside the session index, so a message costs one O_APPEND write instead of rewriting the session record.

func (*Manager) Clear

func (m *Manager) Clear() int

Clear removes all sessions

func (*Manager) Close

func (m *Manager) Close(id string) bool

Close terminates a session gracefully

func (*Manager) Create

func (m *Manager) Create() *Session

Create creates a new session and returns it

func (*Manager) CreateWith

func (m *Manager) CreateWith(chatID, agent, project string) *Session

CreateWith creates a new session with binding information

func (*Manager) CreateWithID added in v0.260514.1

func (m *Manager) CreateWithID(id, chatID, agent, project string) *Session

CreateWithID creates a new session bound to a caller-supplied ID instead of generating a UUID. Used by /resume to align the remote session record with a Claude on-disk session_id so the next message naturally triggers --resume. Returns nil if a session with that ID already exists in memory.

func (*Manager) Delete

func (m *Manager) Delete(id string) bool

Delete removes a session

func (*Manager) FindBy

func (m *Manager) FindBy(chatID, agent, project string) *Session

FindBy finds a session by (chatID, agent, project) tuple. Returns the session if found and not closed/expired, otherwise nil.

func (*Manager) Get

func (m *Manager) Get(id string) (*Session, bool)

Get retrieves a session by ID

func (*Manager) GetMessages

func (m *Manager) GetMessages(id string) ([]Message, bool)

GetMessages retrieves a session's messages, reading the transcript on demand — history is not held in memory for every live session.

func (*Manager) GetOrLoad

func (m *Manager) GetOrLoad(id string) (*Session, bool)

GetOrLoad retrieves a session by ID, falling back to the store if needed

func (*Manager) GetRequest

func (m *Manager) GetRequest(id string) (string, bool)

GetRequest retrieves the request for a session

func (*Manager) GetStats

func (m *Manager) GetStats() map[string]interface{}

GetStats returns comprehensive session statistics

func (*Manager) GetStatus

func (m *Manager) GetStatus(id string) (Status, bool)

GetStatus returns the current status of a session under the manager's lock, giving callers a race-free view without holding onto a *Session pointer.

func (*Manager) List

func (m *Manager) List() []*Session

List returns all sessions

func (*Manager) ListByChat

func (m *Manager) ListByChat(chatID string) []*Session

ListByChat lists all sessions for a given chat ID. Useful for debugging and management.

func (*Manager) SetCompleted

func (m *Manager) SetCompleted(id string, response string) bool

SetCompleted marks a session as completed with response

func (*Manager) SetFailed

func (m *Manager) SetFailed(id string, err string) bool

SetFailed marks a session as failed with error

func (*Manager) SetRequest

func (m *Manager) SetRequest(id string, request string) bool

SetRequest stores the request for a session

func (*Manager) SetRunning

func (m *Manager) SetRunning(id string) bool

SetRunning marks a session as running

func (*Manager) Stats

func (m *Manager) Stats() map[string]int

Stats returns session statistics by status

func (*Manager) Stop

func (m *Manager) Stop()

Stop halts the background loops. Safe to call more than once — shutdown paths can overlap, and closing stopCh twice would panic.

It does not close the store: the manager does not own it (the StoreManager does), and every write is already committed by the time it returns.

func (*Manager) Update

func (m *Manager) Update(id string, fn func(*Session)) bool

Update updates a session

type Message

type Message struct {
	Role      string    // "user" or "assistant"
	Content   string    // Full content
	Summary   string    // Optional summary for assistant responses
	Timestamp time.Time // When the message was created
}

Message represents a chat message within a session

type Session

type Session struct {
	ID             string    // Unique session identifier
	ChatID         string    // NEW: Bound chat ID
	Agent          string    // NEW: Bound agent type ("claude", "tingly-box")
	Project        string    // NEW: Bound project path
	Status         Status    // Current session status
	Request        string    // User's request payload
	Response       string    // Claude Code response summary
	Error          string    // Error message if failed
	CreatedAt      time.Time // Session creation timestamp
	LastActivity   time.Time // Last activity timestamp
	ExpiresAt      time.Time // Session expiration timestamp
	PermissionMode string    // Claude CLI permission mode: "default", "plan", "auto", "acceptEdits", "dontAsk", "bypassPermissions"
}

Session represents an execution session

type SessionStore

type SessionStore interface {
	// Get retrieves a session by ID
	Get(sessionID string) (*Session, error)

	// Set stores a session
	Set(sessionID string, sess *Session) error

	// Delete removes a session
	Delete(sessionID string) error

	// List returns all sessions
	List() []*Session

	// FindByChatAgentProject finds a session by (chatID, agent, project) tuple
	FindByChatAgentProject(chatID, agent, project string) (*Session, error)

	// ListByChat lists all sessions for a given chat ID
	ListByChat(chatID string) ([]*Session, error)

	// AppendMessage adds one message to a session's transcript. Separate from
	// Set because the transcript is append-only and lives outside the session
	// index — see Transcript for why history is not stored as rows.
	AppendMessage(sessionID string, msg Message) error

	// Messages returns a session's full history, read on demand.
	Messages(sessionID string) ([]Message, error)
}

SessionStore defines the interface for session persistence, keeping this package independent of where sessions are actually stored

type Status

type Status string

Status represents the current state of a session

const (
	StatusPending   Status = "pending"
	StatusRunning   Status = "running"
	StatusCompleted Status = "completed"
	StatusFailed    Status = "failed"
	StatusExpired   Status = "expired"
	StatusClosed    Status = "closed"
)

type Transcript added in v0.260801.1

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

Transcript stores a session's message history as one append-only JSONL file per session.

Why files and not rows. Messages are written once, read whole or not at all, never queried by content, and grow without bound with the length of a conversation. That is the opposite of what a table is good for: in SQLite every append becomes a transaction against the database every other part of the product shares, and the database file inflates with conversation text that nothing ever selects on. As a file per session, an append is one O_APPEND write, no session's history sits in another's file, and the transcript stays something a user can tail, grep, and hand to a bug report. (One mutex still serialises this process's writes, so a large message cannot be torn by an interleaving append — that is about write integrity, not about sessions sharing storage.)

This mirrors how Claude Code keeps its own sessions, which matters here beyond taste: a remote session can be bound to a Claude on-disk session id (see Manager.CreateWithID, used by /resume), so the two halves of one conversation stay the same kind of artifact.

What stays in SQLite is the session INDEX — binding, status, timestamps — because that genuinely needs indexed lookup, and it is small and bounded. The split is by access pattern, not by preference for one medium.

func NewTranscript added in v0.260801.1

func NewTranscript(dir string) (*Transcript, error)

NewTranscript creates a transcript store rooted at dir. A blank dir returns (nil, nil): a nil *Transcript is usable and simply drops history, which keeps sessions working in tests and in stores built without a data dir.

func (*Transcript) Append added in v0.260801.1

func (t *Transcript) Append(sessionID string, msg Message) error

Append writes one message to the end of a session's transcript.

O_APPEND means the cost does not grow with the conversation — the defect that made the previous whole-file store quadratic in message count.

func (*Transcript) Delete added in v0.260801.1

func (t *Transcript) Delete(sessionID string) error

Delete removes a session's transcript. A missing file is not an error.

func (*Transcript) Load added in v0.260801.1

func (t *Transcript) Load(sessionID string) ([]Message, error)

Load reads a session's full history. A missing transcript is empty, not an error — a session that never exchanged a message has no file.

A malformed line is skipped rather than failing the whole read: a torn final write (killed mid-append) must not make the preceding history unreadable.

func (*Transcript) Path added in v0.260801.1

func (t *Transcript) Path(sessionID string) string

Path returns the on-disk transcript file for a session.

The id goes through safeFileKey because it is not always ours: Manager.CreateWithID binds a session to a Claude session id supplied by the user through /resume, so "../../etc/x" must not escape the directory.

Jump to

Keyboard shortcuts

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