Documentation
¶
Index ¶
- type Config
- type Manager
- func (m *Manager) AppendMessage(id string, msg Message) bool
- func (m *Manager) Clear() int
- func (m *Manager) Close(id string) bool
- func (m *Manager) Create() *Session
- func (m *Manager) CreateWith(chatID, agent, project string) *Session
- func (m *Manager) CreateWithID(id, chatID, agent, project string) *Session
- func (m *Manager) Delete(id string) bool
- func (m *Manager) FindBy(chatID, agent, project string) *Session
- func (m *Manager) Get(id string) (*Session, bool)
- func (m *Manager) GetMessages(id string) ([]Message, bool)
- func (m *Manager) GetOrLoad(id string) (*Session, bool)
- func (m *Manager) GetRequest(id string) (string, bool)
- func (m *Manager) GetStats() map[string]interface{}
- func (m *Manager) GetStatus(id string) (Status, bool)
- func (m *Manager) List() []*Session
- func (m *Manager) ListByChat(chatID string) []*Session
- func (m *Manager) SetCompleted(id string, response string) bool
- func (m *Manager) SetFailed(id string, err string) bool
- func (m *Manager) SetRequest(id string, request string) bool
- func (m *Manager) SetRunning(id string) bool
- func (m *Manager) Stats() map[string]int
- func (m *Manager) Stop()
- func (m *Manager) Update(id string, fn func(*Session)) bool
- type Message
- type Session
- type SessionStore
- type Status
- type Transcript
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 ¶
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) CreateWith ¶
CreateWith creates a new session with binding information
func (*Manager) CreateWithID ¶ added in v0.260514.1
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) FindBy ¶
FindBy finds a session by (chatID, agent, project) tuple. Returns the session if found and not closed/expired, otherwise nil.
func (*Manager) GetMessages ¶
GetMessages retrieves a session's messages, reading the transcript on demand — history is not held in memory for every live session.
func (*Manager) GetOrLoad ¶
GetOrLoad retrieves a session by ID, falling back to the store if needed
func (*Manager) GetRequest ¶
GetRequest retrieves the request for a session
func (*Manager) GetStatus ¶
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) ListByChat ¶
ListByChat lists all sessions for a given chat ID. Useful for debugging and management.
func (*Manager) SetCompleted ¶
SetCompleted marks a session as completed with response
func (*Manager) SetRequest ¶
SetRequest stores the request for a session
func (*Manager) SetRunning ¶
SetRunning marks a session as running
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.
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 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.