Documentation
¶
Overview ¶
Package session stores a conversation and everything else that outlives one turn: the checkpoint repository, spilled tool output, the steering inbox and the record of background processes. See session.go for the directory layout.
Index ¶
- Constants
- func FirstWordsTitle(prompt string, n int) string
- func FromUI() bool
- func NormalizeTitle(s string) string
- func RunIDFor(name string) string
- type Event
- type Kind
- type Manager
- func (m *Manager) AcquireLock(name string) (func(), error)
- func (m *Manager) ActiveTurns() ([]string, error)
- func (m *Manager) BaseDir() string
- func (m *Manager) DeleteSession(name string) error
- func (m *Manager) GenerateSessionName() string
- func (m *Manager) InboxDirFor(name string) string
- func (m *Manager) LastReport(name string) (*report.Report, bool, error)
- func (m *Manager) ListSessions() ([]SessionInfo, error)
- func (m *Manager) LoadSession(name string) ([]llm.Message, error)
- func (m *Manager) MostRecent() (string, error)
- func (m *Manager) Open(name string) (*Session, error)
- func (m *Manager) ReadMeta(name string) *Meta
- func (m *Manager) SessionExists(name string) bool
- func (m *Manager) SessionTitle(name string) string
- func (m *Manager) ShowSession(name string) (string, error)
- type Meta
- type Session
- func (s *Session) AcquireLock() (func(), error)
- func (s *Session) Append(ev Event) error
- func (s *Session) AppendMessages(messages []llm.Message) error
- func (s *Session) CheckpointsDir() string
- func (s *Session) Dir() string
- func (s *Session) HistoryPath() string
- func (s *Session) InboxDir() string
- func (s *Session) LastReport() (rep *report.Report, current bool, err error)
- func (s *Session) LastSettingsModel() (string, error)
- func (s *Session) Load() ([]llm.Message, error)
- func (s *Session) MarkTurnStart() (func(), error)
- func (s *Session) Meta() *Meta
- func (s *Session) Name() string
- func (s *Session) Notice(text string) error
- func (s *Session) ProcDir() string
- func (s *Session) Rollback(n int) error
- func (s *Session) SaveMeta() error
- func (s *Session) Settings(model string, mergeThinking bool, tools []string) error
- func (s *Session) TmpDir() string
- func (s *Session) TurnPID() (int, bool)
- func (s *Session) TurnPIDPath() string
- type SessionInfo
Constants ¶
const ( // CheckpointsSubdir and friends are exported so callers can build the // paths they hand to other components. CheckpointsSubdir = "checkpoints" ProcSubdir = "proc" InboxSubdir = "inbox" TmpSubdir = "tmp" )
A session is a directory, not a file:
~/.kvit-coder/sessions/<name>/
history.jsonl append-only, one timestamped event per line
meta.json when it was created and last touched, which workspace
and model, the first prompt, and the compaction chain
checkpoints/ the shadow git repo the checkpoint tools commit into
proc/ pidfiles and logs for background processes
inbox/ files dropped here reach the model next iteration
tmp/ tool output too large to put in a message
Everything with a life longer than one turn lives here. That is what lets a turn run in its own process without losing the checkpoint history, the temp files whose paths the model was told, or anything else the next turn needs.
const FromUIVar = "KVIT_CODER_FROM_UI"
FromUIVar marks a kvit-coder run spawned by kvit-coder-ui. The UI already showed its own banner and echoed the prompt, so the child skips its copy rather than printing both.
const MaxTitleChars = 80
MaxTitleChars caps a title's length. A summarizer asked for 3-6 words stays far below it; it only trims a pathological answer or a very long fallback prompt.
const ReportToolName = "Report"
ReportToolName is the tool whose arguments are a structured report. The name is the link between the agent that submits one and the front end that draws it, so it is written down once here.
const RunIDVar = "KVIT_RUN_ID"
RunIDVar is the environment variable the LLM client's ${KVIT_RUN_ID} header placeholder expands from. Endpoints that route by a session header put every request carrying the same value on one backend.
const TitleFallbackWords = 5
TitleFallbackWords is how many leading words of the prompt become the session title when no summarizer model is configured or its call fails.
Variables ¶
This section is empty.
Functions ¶
func FirstWordsTitle ¶
FirstWordsTitle returns the prompt's first n words, joined by single spaces. It is the title fallback when the summarizer model is missing or its call fails. n <= 0 selects TitleFallbackWords. It returns "" for an empty prompt (a wake turn carries none, so there is nothing to title).
func NormalizeTitle ¶
NormalizeTitle trims a raw title down to what is stored in meta.json: the first non-empty line, without surrounding quotes or backticks, whitespace collapsed, capped at MaxTitleChars. It returns "" when nothing remains, so the caller can fall back.
Types ¶
type Event ¶
type Event struct {
TS time.Time `json:"ts"`
Kind Kind `json:"kind"`
// Message is set on KindMessage and KindToolResult. Load returns these,
// in order, as the conversation to send to the model, so the API history
// is derived from the record rather than stored separately.
Message *llm.Message `json:"message,omitempty"`
// Tool and MS accompany KindToolResult.
Tool string `json:"tool,omitempty"`
MS int64 `json:"ms,omitempty"`
// Text accompanies KindNotice.
Text string `json:"text,omitempty"`
// Model, MergeThinking and Tools accompany KindSettings. Tools is what was
// registered for the turn, which is the only record of what the model was
// offered: the configuration that produced it can change between one
// session and the next, so counting calls against today's config answers
// a question about today rather than about the session being read.
Model string `json:"model,omitempty"`
MergeThinking *bool `json:"merge_thinking,omitempty"`
Tools []string `json:"tools,omitempty"`
// Dropped accompanies KindRollback: how many messages at the end of the
// history so far are no longer part of the conversation.
Dropped int `json:"dropped,omitempty"`
}
Event is one line of history.jsonl. One JSON object per line keeps the file greppable and appendable without reading what is already there.
type Kind ¶
type Kind string
Kind names what a history line records.
const ( // KindMessage is one message of the conversation as the API sees it. KindMessage Kind = "message" // KindToolResult is a message that is a tool's result, with which tool // produced it and how long it took. KindToolResult Kind = "tool_result" // KindNotice is something that happened to the turn rather than in the // conversation: the iteration budget running out, an interrupt, a // background process exiting. KindNotice Kind = "notice" // KindSettings records the model and options a turn ran with, so a // transcript read later says what produced it. KindSettings Kind = "settings" // KindRollback records that the loop discarded the last few messages, // which is how backtracking appears in a file that is only ever appended // to. Load applies it; the discarded attempt stays in the file, where it // says what the model tried and why it was taken back. KindRollback Kind = "rollback" )
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the directory sessions live in.
func NewManagerIn ¶
NewManagerIn returns a manager over a named sessions directory, creating it if it is not there. NewManager is this with the usual location; a caller that keeps sessions somewhere else says so here.
func (*Manager) AcquireLock ¶
AcquireLock takes an exclusive lock on a session by name. The lock file lives inside the session directory, alongside the history it protects.
func (*Manager) ActiveTurns ¶
ActiveTurns returns the sessions that have a live turn running, so a command given no session name can find the one to talk to.
func (*Manager) DeleteSession ¶
DeleteSession removes a session and everything it holds: history, metadata, checkpoints, and the temp files whose cleanup now happens here rather than at process exit.
func (*Manager) GenerateSessionName ¶
GenerateSessionName generates a unique session name in YYYY-MM-DD-random6 format. Uses crypto/rand for secure random suffix generation.
func (*Manager) InboxDirFor ¶
InboxDirFor is where a message for the named session is dropped, without opening the session or creating anything else it needs.
func (*Manager) LastReport ¶
LastReport returns the most recent structured report in the named session, and whether it is still that conversation's last message. It is what the interactive front end asks between turns, without opening the session or taking its lock: a report is only ever read here, never written.
A session that does not exist has no report, which is not an error.
func (*Manager) ListSessions ¶
func (m *Manager) ListSessions() ([]SessionInfo, error)
ListSessions returns a list of all sessions with metadata, newest first. Sessions not yet migrated out of the flat-file layout are listed too.
func (*Manager) LoadSession ¶
LoadSession returns a session's conversation without opening or migrating it, for listing and display.
func (*Manager) MostRecent ¶
MostRecent returns the name of the session touched last, or "" when there are none. It is what "continue where I left off" resolves to.
func (*Manager) ReadMeta ¶ added in v0.2.0
ReadMeta returns the named session's meta.json, or nil when it has none or it cannot be read. Like readMetaTitle it never creates or migrates anything.
func (*Manager) SessionExists ¶
SessionExists reports whether the named session has any history, either as a session directory or as a flat file from before sessions were directories.
func (*Manager) SessionTitle ¶
SessionTitle returns the named session's display title, or "" when it has none yet. A session gets its title when its first turn finishes, so a caller that shows it has to ask again rather than reading it once at startup.
type Meta ¶
type Meta struct {
Name string `json:"name"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Workspace string `json:"workspace,omitempty"`
Model string `json:"model,omitempty"`
FirstPrompt string `json:"first_prompt,omitempty"`
// Title is the session's display title: a 3-6 word summary of the
// first prompt from the summarizer model, or the prompt's first
// words when no summarizer is configured or its call fails. Set once
// at the start of a session (see internal/repl/title.go) and left
// alone afterwards; Name stays the YYYY-MM-DD-random6 directory label.
Title string `json:"title,omitempty"`
// Cost is what the session's turns have cost so far, summed, as the
// endpoint reports it (llm.generation_stats); zero when it reports none.
Cost float64 `json:"cost,omitempty"`
Summary string `json:"summary,omitempty"`
Parent string `json:"parent,omitempty"`
SucceededBy string `json:"succeeded_by,omitempty"`
}
Meta is meta.json: what this session is, and what it was compacted from or into. Parent and SucceededBy are written by compaction, which does not exist yet; Open already follows SucceededBy so that adding it later needs no change here.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one open session directory.
func Open ¶
Open opens or creates the named session under the default sessions directory, migrating a session left over from when a session was a single flat file, and following the compaction chain to the session that succeeded this one.
func OpenDir ¶
OpenDir opens or creates a session at an explicit directory. The benchmark harnesses use it to give a run an ephemeral session under its own working directory, so the same code path serves them and a named session alike.
func (*Session) AcquireLock ¶
AcquireLock takes an exclusive lock on this session, so two processes do not append to one history at once. The returned function releases it.
func (*Session) Append ¶
Append writes one event to history.jsonl. The file is opened in append mode for each call, so a second process writing to the same session interleaves whole lines rather than overwriting.
func (*Session) AppendMessages ¶
AppendMessages records a run of conversation messages, one event each. A message that is a tool result is recorded as such, so the transcript says which tool produced it.
func (*Session) CheckpointsDir ¶
CheckpointsDir is the shadow git repository the checkpoint tools commit into.
func (*Session) HistoryPath ¶
HistoryPath is the append-only transcript.
func (*Session) LastReport ¶
LastReport returns the most recent structured report in this session's history, and whether it is still the conversation's last message.
Nothing about a report is stored separately: the model submitted it as the arguments of a Report tool call, so it is already in history.jsonl as part of an ordinary assistant message. Reading it back from there means there is one copy of it rather than two that can drift.
"Current" — the second return value — is what decides whether the front end draws the card. It is true when nothing follows the report except its own tool result, which is what makes tracking unnecessary: a cancelled turn, an exhausted iteration budget and an answered report all append messages of their own, so each of them takes the card away by being there.
A report that fails to parse is skipped rather than returned as an error: the worst case is a card that does not appear, and refusing to open a session over one malformed line would be worse.
func (*Session) LastSettingsModel ¶
LastSettingsModel returns the model the most recent turn recorded in its settings event ("model:effort" once model selection exists, a bare wire id before it), or "" when no turn has recorded one yet. A turn that switches models compares this against its own to decide whether the tail of the history was produced by a different model.
func (*Session) Load ¶
Load returns the conversation, in order, as the messages to send to the model. Lines that carry no message — notices and settings — are skipped, so the API history is derived from the record rather than kept beside it.
func (*Session) MarkTurnStart ¶
MarkTurnStart records this process as the one running the session's turn. The returned function removes the record and must be called when the turn ends, however it ends.
func (*Session) Meta ¶
Meta returns the session's metadata. The pointer is the session's own, so a caller can set fields and then call SaveMeta.
func (*Session) Notice ¶
Notice records something that happened to the turn rather than in the conversation.
func (*Session) Rollback ¶
Rollback records that the loop discarded the last n messages. The messages stay in the file — it is append-only — and Load skips them.
func (*Session) Settings ¶
Settings records what this turn ran with, so a transcript read weeks later says which model, options and tools produced it.
func (*Session) TmpDir ¶
TmpDir holds tool output too large to put in a message. It outlives the turn because the model is told these paths and may read them on a later turn.
func (*Session) TurnPID ¶
TurnPID returns the process ID of the turn running for this session, and whether that process is still alive. A pidfile left behind by a process that died reports false, so a crash does not make a session look busy forever.
func (*Session) TurnPIDPath ¶
TurnPIDPath is where the running turn records its process ID.
type SessionInfo ¶
type SessionInfo struct {
Name string
ModTime time.Time
MessageCount int
// Title is the session's display title from meta.json ("" when the
// session predates titles or has none yet). Name stays the
// YYYY-MM-DD-random6 directory label.
Title string
}
SessionInfo contains metadata about a session.