sessions

package
v0.1.708 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Overview

Package sessions lists the agents' recent sessions from their own session files — Claude Code's projects/*/<id>.jsonl (and Qoder's, the same kind), Codex's rollout files, OpenCode's database (or its older JSON files) and ZCode's, Pi's session files and omp's, DeepSeek Harness's, Cline's, Grok Build's, WorkBuddy's and Cursor CLI's chat stores — with the tokens each spent, what that cost at the effective price, and the command that resumes it. It only ever reads the agents' folders.

The files grow long (hundreds of MB), so each one's parse is kept by path, size and time, and a file that only grew is read on from where it was left. The parses are kept on disk too, in magpie's cache folder.

Index

Constants

View Source
const ActiveWindow = time.Minute

ActiveWindow is how recently a session's file may have been written for it to count as in use, and not be deleted.

View Source
const Limit = 200

Limit is how many sessions, the latest by last activity, List reads.

Variables

View Source
var ErrActive = errors.New("the session was written to in the last minute; it may still be running")

ErrActive is a session still being written: its agent may be running it.

View Source
var PriceOf = priceOf

PriceOf is the effective price of a model as a session names it, at the settings given. Tests swap it; the settings are the ones the listing read, so a swapped one prices against the same copy as the default does.

Functions

func CachePath

func CachePath() string

CachePath is where the parses are kept between runs.

func ClaudeDir

func ClaudeDir() string

ClaudeDir is Claude Code's folder: $CLAUDE_CONFIG_DIR, else ~/.claude.

func ClineSessionDir added in v0.1.310

func ClineSessionDir() string

ClineSessionDir is Cline's sessions folder: $CLINE_SESSION_DATA_DIR, else sessions in $CLINE_DATA_DIR, else in data in $CLINE_DIR, else in ~/.cline/data.

func CodexDir

func CodexDir() string

CodexDir is Codex's folder: $CODEX_HOME, else ~/.codex.

func CodexTitles added in v0.1.591

func CodexTitles(ids []string) map[string]string

CodexTitles reads Codex's append-only name index, never its configuration or conversation contents. Later entries replace earlier names after a rename. The result contains only the requested IDs; missing names remain unknown.

func CursorDir added in v0.1.661

func CursorDir() string

CursorDir is Cursor's CLI config folder: $CURSOR_CONFIG_DIR, else cursor in $XDG_CONFIG_HOME, else ~/.cursor.

func Deletable added in v0.1.593

func Deletable(agent string) bool

Deletable says whether magpie can move an agent's sessions to its trash: those kept as files of their own, in a layout magpie knows whole.

func DesktopDataDirs added in v0.1.579

func DesktopDataDirs() []string

DesktopDataDirs are the folders whose session metadata belongs to this index.

func Dirs added in v0.1.289

func Dirs() []string

Dirs are the folders the sessions are read from: Claude Code's and Codex's, and the other agents' where they keep sessions on this computer.

func DshDir added in v0.1.310

func DshDir() string

DshDir is dsh's folder: $DSH_HOME, else ~/.dsh.

func Duration added in v0.1.265

func Duration(sec int64) string

Duration is active seconds in hours and minutes: 3h 12m, 40m, <1m; — when not kept.

func GrokDir added in v0.1.313

func GrokDir() string

GrokDir is Grok Build's folder: $GROK_HOME, else ~/.grok.

func HermesDir added in v0.1.669

func HermesDir() string

Hermes stores canonical, non-overlapping input/cache buckets. Reasoning is already included in output. session_model_usage includes auxiliary tasks and multiple billing routes; sessions supplies only unattributed main-loop usage. These tables contain aggregates, not individual API request usage.

func HermesDirs added in v0.1.669

func HermesDirs() []string

HermesDirs includes the default store and live named profiles. A selected profile home resolves back to its root, as Hermes's profile resolver does.

func OmpDir added in v0.1.490

func OmpDir() string

OmpDir is omp's agent folder, ~/.omp/agent, as magpie's omp agent has it.

func OpenCodeDir added in v0.1.289

func OpenCodeDir() string

OpenCodeDir is OpenCode's data folder: $XDG_DATA_HOME/opencode, else ~/.local/share/opencode — on Windows too, where OpenCode keeps it there.

func PiDir added in v0.1.289

func PiDir() string

PiDir is Pi's agent folder: $PI_CODING_AGENT_DIR, else ~/.pi/agent.

func Purge added in v0.1.615

func Purge(key string) error

Purge erases a trashed session for good: its folder in magpie's trash, files and note. Only when the reader asks for it (#487); nothing is ever erased by itself. A key that isn't a trashed session's, or whose folder (or its agent's) is a link that could lead out of the trash, is refused.

func QoderDir added in v0.1.310

func QoderDir(id string) string

QoderDir is the folder of Qoder's build of this id: $QODER_CONFIG_DIR, else ~/.qoder, for qoder; $QODERCN_CONFIG_DIR, else ~/.qoder-cn, for qoder-cn.

func Reset

func Reset()

Reset forgets the kept parses, in memory only.

func ResumeCommand

func ResumeCommand(agent, id, cwd string) string

ResumeCommand is the shell line that picks a session up again in its folder, or "" for an id that isn't plain.

func Saved added in v0.1.417

func Saved()

Saved waits for the index being saved, so a process about to end leaves it on disk.

func SessionGlob added in v0.1.579

func SessionGlob(pattern string) ([]string, error)

SessionGlob is filepath.Glob for session discovery, with directory listings cached by metadata. It never caches file contents or suppresses new entries.

func ToolCategory added in v0.1.410

func ToolCategory(name string) string

ToolCategory is the kind of tool a name is, Claude Code's and Codex's alike: Bash, Edit, Read, Write, Glob, Grep, Task (subagents and plans), Tool (an MCP server's) or Other.

func TraceAgent added in v0.1.679

func TraceAgent(agent string) bool

TraceAgent lists formats with an interaction and tool-aware trace adapter.

func TraceID added in v0.1.679

func TraceID(parts ...string) string

func TraceSessionVisible added in v0.1.701

func TraceSessionVisible(agent, id string) bool

TraceSessionVisible resolves a first request before the next reader tick. Only formats with an authoritative header are eligible; filenames alone cannot establish that a usable local transcript exists.

func TrashDir added in v0.1.593

func TrashDir() string

TrashDir is where deleted sessions are kept: <magpie dir>/trash/sessions.

func WorkBuddyDir added in v0.1.313

func WorkBuddyDir() string

WorkBuddyDir is WorkBuddy's folder: $WORKBUDDY_CONFIG_DIR, else ~/.workbuddy.

func ZCodeDir added in v0.1.310

func ZCodeDir() string

ZCodeDir is ZCode's folder, ~/.zcode.

Types

type Active added in v0.1.239

type Active struct {
	Agent   string `json:"agent"`
	Cwd     string `json:"cwd"`
	Seconds int64  `json:"seconds"`
	// Hours is Seconds again by the local hour of the day (24, or none)
	Hours []int64 `json:"hours,omitempty"`
}

Active is the time an agent's sessions in one folder were at work on a day, in seconds (see day.Active for how it is told).

type AgentCount added in v0.1.593

type AgentCount struct {
	Agent     string `json:"agent"`
	Count     int    `json:"count"`
	Deletable bool   `json:"deletable"`
}

AgentCount is an agent with sessions on this computer, and how many.

func Agents added in v0.1.593

func Agents() []AgentCount

Agents are the agents with sessions on this computer, by how many.

type Call added in v0.1.579

type Call struct {
	Time    time.Time `json:"t"`
	Agent   string    `json:"agent"`   // claude (Claude Code's CLI), claude-desktop (Desktop's Code tab and Cowork), codex
	Session string    `json:"session"` // the id the agent sends the gateway as its session header
	Model   string    `json:"model"`   // the model the file names: for Claude, the one the vendor answered
	// Requested: the model Claude Code was running as (its identity note) when it made
	// the call, as it was asked for: claude-opus-5[1m] for claude-opus-5 with the long context
	Requested string `json:"requested,omitempty"`
	Tokens
	Reasoning int    `json:"reasoning,omitempty"`  // Codex: inside Output already
	Effort    string `json:"effort,omitempty"`     // the thinking effort: Codex's, of the turn; Claude's, of the line
	RequestID string `json:"request_id,omitempty"` // Claude's
	// Codex's session metadata identifies its configured provider and creator.
	// These identify the historical session, not whichever account is signed in now.
	Upstream  string `json:"upstream,omitempty"`
	AccountID string `json:"account_id,omitempty"`
	UserID    string `json:"user_id,omitempty"`
	Error     string `json:"error,omitempty"`      // Claude: the API error's kind (rate_limit, server_error …); such a call has no tokens
	ErrorText string `json:"error_text,omitempty"` // its message, the first 400 characters
	// Codex: the turn's own figures, set only when the turn made just this call
	TTFT int64 `json:"ttft_ms,omitempty"`
	// Millis: how long the call took. Codex's, of a turn that made this call alone, is what
	// it says; the rest are told from the file's times, the call's last line less the line
	// that asked for it (a prompt, a tool's result), and so a little more or less than it was
	Millis int64  `json:"ms,omitempty"`
	Cwd    string `json:"cwd,omitempty"`
	// Where the call is in its file, for reading what was said in it (ContentOf): the file,
	// from where what it was asked began — after the reply before it — to where it ended,
	// and for Claude Code the id of its message
	File string `json:"file,omitempty"`
	From int64  `json:"from,omitempty"`
	To   int64  `json:"to,omitempty"`
	Msg  string `json:"msg,omitempty"`
}

Call is one model call as an agent's own session file records it.

func Calls added in v0.1.579

func Calls(since time.Time) []Call

Calls reads per-file request shards, separately from the session summaries. Only changed files are scanned, and only a bounded number of parses stay in memory. Conversation text is read by ContentOf when a detail is opened.

func FindCall added in v0.1.579

func FindCall(session string, from, to, at time.Time) (Call, bool)

FindCall is the call of a session made between two times that ended nearest at (a call's Time is its last line's), if the session files have one. A gateway request's window can hold the next calls of a quick tool loop too; the newest of them would be another request's.

func ReadCallSource added in v0.1.579

func ReadCallSource(s CallSource) []Call

ReadCallSource returns immutable calls for one source. Independent files can be read concurrently; disk IO never holds callsMu.

type CallSource added in v0.1.579

type CallSource struct {
	Path, Key, Agent string
	Size             int64
	Modified         time.Time
}

CallSource identifies a raw local request log without reading its contents.

func CallSources added in v0.1.579

func CallSources() []CallSource

type Content added in v0.1.579

type Content struct {
	Input  []Part `json:"input"`
	Output []Part `json:"output"`
	Cut    bool   `json:"cut,omitempty"` // there was more than is kept here
}

Content is what a call was given and what it said, in the order of the file.

func ContentOf added in v0.1.579

func ContentOf(c Call) (Content, error)

ContentOf reads what was said in a call from its file.

type Day added in v0.1.239

type Day struct {
	Date   string   `json:"date"`
	Usage  []Usage  `json:"usage"`  // by agent, folder and model
	Active []Active `json:"active"` // by agent and folder
}

Day is one local date's usage, cut fine enough for the page to filter it by agent, model and folder itself.

type DayTotal added in v0.1.265

type DayTotal struct {
	Date string `json:"date"`
	Tokens
	Cost   float64 `json:"cost"`
	Active int64   `json:"active"` // seconds; -1 under a model
}

DayTotal is one date's usage under a Rollup's filters.

type Managed added in v0.1.593

type Managed struct {
	Session
	Size      int64 `json:"size"`
	Messages  int   `json:"messages"`
	Files     int   `json:"files"`
	Deletable bool  `json:"deletable"`
}

Managed is a session as the management page lists it: what List has, and how big its files are, how many messages it has, and whether it can be deleted.

func ListAgent added in v0.1.593

func ListAgent(agent string) []Managed

ListAgent is every session of one agent, the most recently active first, with no limit; one nothing was said in is listed too, to be cleared away.

type Model

type Model struct {
	Model string `json:"model"`
	Tokens
	Cost   float64 `json:"cost"`
	Priced bool    `json:"priced"`
}

Model is one model's share of a session.

type Overview added in v0.1.410

type Overview struct {
	Count  int   `json:"count"`
	Median int   `json:"median"` // tokens (in and out) of the middle session
	P90    int   `json:"p90"`    // … of the one nine in ten spent no more than
	Days   []int `json:"days"`   // sessions at work each day, from Stats.From to To
	// Top are the sessions that spent the most, by tokens, cost and
	// active time, n of each
	Top map[string][]Summary `json:"top"`
	// Messages and Output are the messages and output tokens each day,
	// as Days
	Messages []int `json:"messages"`
	Output   []int `json:"output"`
	// Shape is how many sessions came to how much: by messages, by
	// minutes at work and by tool calls a prompt (autonomy)
	Shape  map[string]Shape `json:"shape"`
	Tools  ToolUse          `json:"tools"`
	Skills SkillUse         `json:"skills"`
}

Overview is what the sessions of a range come to under the page's filters.

type Part added in v0.1.579

type Part struct {
	Role string `json:"role"`           // user, assistant or tool
	Kind string `json:"kind"`           // text, thinking, tool_use, tool_result, context or image
	Name string `json:"name,omitempty"` // the tool's, of its call
	Text string `json:"text"`
	Cut  int    `json:"cut,omitempty"` // the characters left off its end
}

Part is one thing said in a call.

type Progress added in v0.1.410

type Progress struct {
	Indexing bool  `json:"indexing"`
	Files    int64 `json:"files"`
	Done     int64 `json:"done"`
	Bytes    int64 `json:"bytes"`
	Read     int64 `json:"read"`
}

Progress is how far the sessions being read have got; Indexing is false when none are.

func Indexing added in v0.1.410

func Indexing() Progress

Indexing is the reading's progress.

type Rollup added in v0.1.265

type Rollup struct {
	From   string `json:"from"`
	To     string `json:"to"`
	Model  string `json:"model,omitempty"`
	Folder string `json:"folder,omitempty"`
	Tokens
	Cost     float64  `json:"cost"`     // USD at the effective price, of the priced models
	Unpriced []string `json:"unpriced"` // models that spent tokens and have no known price
	// Active is the seconds the sessions were at work; -1 under a model,
	// as active time isn't kept by model.
	Active   int64      `json:"active"`
	DaysUsed int        `json:"days_used"` // dates with usage
	Days     []DayTotal `json:"days"`      // every date From…To, empty ones too
	Models   []Share    `json:"models"`    // under the folder, the most tokens first
	Folders  []Share    `json:"folders"`   // under the model, the most tokens first
}

Rollup is Stats summed under a model and a folder ("" for every one), as the app's Sessions view sums them: for the CLI and the TUI.

type Session

type Session struct {
	ReadOnly bool      `json:"read_only,omitempty"`
	Agent    string    `json:"agent"` // magpie agent id: claude, codex, opencode, pi, omp, zcode, dsh, cline, qoder, qoder-cn, grok, workbuddy, cursor, hermes
	ID       string    `json:"id"`
	Cwd      string    `json:"cwd"`
	Title    string    `json:"title"` // the first prompt, else the agent's own title
	Start    time.Time `json:"start"`
	Last     time.Time `json:"last"`
	Models   []Model   `json:"models"`
	Tokens
	Cost     float64 `json:"cost"`     // USD at the effective price, for the priced models
	Unpriced int     `json:"unpriced"` // models that spent tokens but have no known price
	Resume   string  `json:"resume"`   // the command that picks the session up again
	Path     string  `json:"path"`     // its (main) file
}

Session is one agent session.

func Find

func Find(agent, id string) (Session, bool)

Find is a session by agent and id: a listed one, else one Get finds under agent:id.

func Get added in v0.1.410

func Get(key string) (Session, bool)

Get is the session whose files are grouped under key (a Summary's Key), however long ago it was at work.

func List

func List(limit int) []Session

List reads the latest sessions of every agent, the most recently active first, at most limit of them (Limit when 0).

type Shape added in v0.1.410

type Shape struct {
	Edges  []int `json:"edges"`
	Counts []int `json:"counts"`
	Total  int   `json:"total"`
}

Shape counts sessions into buckets: Counts[i] those from Edges[i] up to the next edge, the last one open.

type Share added in v0.1.265

type Share struct {
	Name string `json:"name"`
	Tokens
	Cost float64 `json:"cost"`
}

Share is one model's or folder's part of a Rollup.

type Skill added in v0.1.410

type Skill struct {
	Name     string         `json:"name"`
	Calls    int            `json:"calls"`
	Sessions int            `json:"sessions"`
	Last     string         `json:"last"`     // the date it was last called
	Agents   map[string]int `json:"agents"`   // calls by agent
	Projects []ToolStat     `json:"projects"` // the folders that called it most, by name
}

Skill is one skill's calls.

type SkillUse added in v0.1.410

type SkillUse struct {
	Calls int     `json:"calls"`
	Top   []Skill `json:"top"`
	Count int     `json:"count"` // skills called at all
}

SkillUse is the skills the sessions called up.

type Stats added in v0.1.239

type Stats struct {
	From string `json:"from"` // the first date, local: the range's, or for all time the first with anything
	To   string `json:"to"`   // today
	Days []Day  `json:"days"` // the dates with anything, in order
	// Sessions are the sessions at work in the range, each with what it
	// spent in it, the most recently active first. Too many to send the
	// page over a long range: Overview sums them up.
	Sessions []Summary `json:"-"`
}

Stats is what every session spent, day by day: all of them, not only the latest List reads.

func StatsAt added in v0.1.265

func StatsAt(days int, now time.Time) Stats

StatsAt is StatsFor as if it were now.

func StatsFor added in v0.1.239

func StatsFor(days int) Stats

StatsFor reads the usage of the last days days up to today, today included (every day when 0), from every session file there is. Only the files written to since the range began are looked at; what is read is kept, as List's is.

func (Stats) Overview added in v0.1.410

func (st Stats) Overview(agent, model, cwd string, n int) Overview

Overview sums up st's sessions of agent, with model and in folder cwd ("" for any), with the n that spent the most each way.

func (Stats) Sum added in v0.1.265

func (s Stats) Sum(model, folder string) Rollup

Sum rolls the stats up under a model and a folder, "" for every one.

type Summary added in v0.1.410

type Summary struct {
	Key   string    `json:"key"` // agent:id as its files are grouped, for Get
	Agent string    `json:"agent"`
	ID    string    `json:"id"`
	Cwd   string    `json:"cwd"`
	Title string    `json:"title"`
	Last  time.Time `json:"last"`
	Tokens
	Cost   float64  `json:"cost"`
	Priced bool     `json:"priced"` // every model it used has a price
	Active int64    `json:"active"` // seconds
	Models []string `json:"models"` // the models it used in the range, the most used first
	Days   []int    `json:"days"`   // the dates it was at work on, as days after From
	// Prompts and Replies are the messages each way, ToolCalls the tools
	// it called, in the range (Claude Code's, Codex's, Pi's and omp's sessions)
	Prompts   int `json:"prompts"`
	Replies   int `json:"replies"`
	ToolCalls int `json:"tool_calls"`
	// contains filtered or unexported fields
}

Summary is one session's share of a range.

type Tokens

type Tokens struct {
	Input      int `json:"input"`
	Output     int `json:"output"`
	CacheRead  int `json:"cache_read"`
	CacheWrite int `json:"cache_write"`
}

Tokens is a count of tokens. Input excludes what was read from cache.

func (Tokens) Spent added in v0.1.265

func (t Tokens) Spent() int

Spent is the tokens a Rollup counts: input and output, as the app does.

type ToolStat added in v0.1.410

type ToolStat struct {
	Name     string `json:"name"`
	Category string `json:"category,omitempty"`
	Calls    int    `json:"calls"`
	Sessions int    `json:"sessions"`
}

ToolStat is one tool's (or category's) calls.

type ToolUse added in v0.1.410

type ToolUse struct {
	Calls      int        `json:"calls"`
	Sessions   int        `json:"sessions"`
	Top        []ToolStat `json:"top"`        // the most called
	Categories []ToolStat `json:"categories"` // by category, the most called first
	// Weeks are the calls week by week, from Monday, by category
	Weeks []ToolWeek `json:"weeks"`
}

ToolUse is what the sessions called their tools for.

type ToolWeek added in v0.1.410

type ToolWeek struct {
	Start string         `json:"start"` // its Monday
	Calls map[string]int `json:"calls"`
}

ToolWeek is one week's calls by category.

type TraceReader added in v0.1.679

type TraceReader struct {
	Since time.Time
	// contains filtered or unexported fields
}

TraceReader follows supported local agent stores modified since activation. Existing lines establish ancestry, but events older than Since aren't emitted. Deterministic IDs make resumed rollouts and copied session branches idempotent.

func NewTraceReader added in v0.1.679

func NewTraceReader(since time.Time) *TraceReader

func (*TraceReader) Poll added in v0.1.679

func (r *TraceReader) Poll(bodies bool) []TraceSpan

Poll reads appended complete lines only. It limits watched files to the most recent sessions; a partial last line remains for the following poll.

func (*TraceReader) SetSessionIndex added in v0.1.701

func (r *TraceReader) SetSessionIndex(index *TraceSessionIndex)

Share request-side discovery with this reader's regular polling.

func (*TraceReader) VisibleSessions added in v0.1.701

func (r *TraceReader) VisibleSessions() []TraceSession

VisibleSessions is refreshed on Poll; old transcripts only contribute their identity, not completed historical operations.

type TraceSession added in v0.1.701

type TraceSession struct{ Agent, ID string }

TraceSession names a readable local transcript, independently of whether it has produced any observations. Header inspection never emits history/bodies.

type TraceSessionIndex added in v0.1.701

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

TraceSessionIndex shares one discovery snapshot across all request IDs and polling. The caller that wins refresh performs directory I/O; other concurrent requests use the snapshot without waiting for that scan. Its zero value is usable; no state is shared between exporters or stores.

func (*TraceSessionIndex) Visible added in v0.1.701

func (x *TraceSessionIndex) Visible(agent, id string) bool

type TraceSpan added in v0.1.679

type TraceSpan struct {
	Agent, Session, Turn, ID, Parent, Name, Kind, Model, Provider, Served string
	Start, End                                                            time.Time
	Input, Output                                                         string
	Tokens                                                                Tokens
	Reasoning                                                             int
	Error, Inferred                                                       bool
	Update                                                                bool
}

TraceSpan is a recorded agent operation, model response, or tool execution.

type Trashed added in v0.1.593

type Trashed struct {
	Key     string    `json:"key"` // its folder in the trash, <agent>/<name>
	Agent   string    `json:"agent"`
	ID      string    `json:"id"`
	Title   string    `json:"title"`
	Cwd     string    `json:"cwd"`
	Last    time.Time `json:"last"`
	Deleted time.Time `json:"deleted"`
	Size    int64     `json:"size"`
	Items   []moved   `json:"items"`
}

Trashed is a deleted session in magpie's trash.

func Delete added in v0.1.593

func Delete(agent, id string) (Trashed, error)

Delete moves a session's files to magpie's trash: its own file, the folder of its subagents and tool results beside it, its other files (a Codex session's later segments, its subagents' elsewhere) and, for Claude Code, the file history, todos and environment it keeps by the session's id. A session any file of which was written in the last minute is not touched (ErrActive).

func Restore added in v0.1.593

func Restore(key string) (Trashed, error)

Restore moves a trashed session's files back where they were. Nothing is moved when any of those places is taken again.

func Trash added in v0.1.593

func Trash() []Trashed

Trash is what is in magpie's session trash, the latest deleted first.

type Usage added in v0.1.239

type Usage struct {
	Agent string `json:"agent"`
	Cwd   string `json:"cwd"`
	Model string `json:"model"`
	Tokens
	Cost   float64 `json:"cost"` // USD at the effective price, when priced
	Priced bool    `json:"priced"`
}

Usage is what one model spent for one agent in one folder on a day.

Jump to

Keyboard shortcuts

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