unified

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClaudeSessionTitleByID

func ClaudeSessionTitleByID(sessionID string) string

ClaudeSessionTitleByID resolves the AUTHORITATIVE display title of a Claude session from its transcript by session id: custom-title → ai-title → summary ONLY, dropping the first-user-message fallback. Used by the daemon on SessionStart to title a resumed tab. Skipping the fallback is deliberate:// feeding the first prompt here would lock the tab's naming lifecycle (SetSessionTitle → Meaningful=true) and suppress the daemon's AI title derivation (Phase B), leaving the tab stuck on the first prompt. An unnamed session (no custom/ai/summary) returns "" so Phase B derives a meaningful name. The Projects list keeps the fallback via extractCwdAndTitle.

func CodexSessionTitleByID

func CodexSessionTitleByID(sessionID string) string

CodexSessionTitleByID resolves the display title of a Codex rollout by session id (first real user prompt). Confirms the id via readCodexMeta so a coincidental filename suffix can't mis-attribute. Used on SessionStart.

func EvictMetaCacheEntry

func EvictMetaCacheEntry(path string)

EvictMetaCacheEntry drops one entry by transcript path — used by the delete handler when a Claude transcript is removed (path is known). Codex/Grok deletes rely on the background reap-missing sweep (their paths aren't known at delete time). No-op if the path was never cached.

func GrokSessionTitleByID

func GrokSessionTitleByID(sessionID string) string

GrokSessionTitleByID resolves the display title of a Grok session from its summary.json by session id (session_summary). Used on SessionStart.

func InitMetaCache

func InitMetaCache()

InitMetaCache loads the persisted cache into the package singleton and starts the background persist+GC+reap tick. Call once at daemon startup alongside LoadKnown. Idempotent (sync.Once).

func InitStatsCache

func InitStatsCache()

InitStatsCache loads the persisted stats cache into the package map + starts the persist/GC tick. Call once at daemon startup alongside InitMetaCache. Idempotent (sync.Once).

func LatestCodexSessionForCwd

func LatestCodexSessionForCwd(cwd string) string

LatestCodexSessionForCwd returns the session id of the most recently modified codex rollout whose session_meta.cwd matches cwd (case-insensitive). Codex stores rollouts at ~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl; the first line is {"type":"session_meta","payload":{"id":"<uuid>","cwd":"…"}}. Returns "" if no recent match is found.

func LatestGrokSessionForCwd

func LatestGrokSessionForCwd(cwd string) string

LatestGrokSessionForCwd returns the session id of the most recently modified grok session directory under the URL-encoded cwd. Grok stores sessions at ~/.grok/sessions/<url-encoded-cwd>/<session-uuid>/. Returns "" if no recent match is found.

func LatestPiSessionForCwd

func LatestPiSessionForCwd(cwd string) string

LatestPiSessionForCwd returns the session id of the most recently modified pi session whose header cwd matches cwd (case-insensitive), within the discovery recent-cutoff. Hookless-agent discovery (pi has no SessionStart hook) — matches LatestCodex/LatestGrokSessionForCwd. Returns "" if none.

func MetaCacheCounters

func MetaCacheCounters() (hits, misses, parses uint64)

MetaCacheCounters exposes (hits, misses, parses) for the warm-skip / "N devices → 1 parse" acceptance checks + debug.

func PiSessionTitleByID

func PiSessionTitleByID(sessionID string) string

PiSessionTitleByID resolves the AUTHORITATIVE display name of a pi session from its transcript by session id: the latest session_info.name ONLY (the user/agent-set name), NOT the first-user-message fallback. Used by the daemon on SessionStart to title a tab that just resumed/loaded a session.

Deliberately skips the first-message fallback (unlike readPiMeta, which the Projects list uses for display): feeding the first prompt here would lock the tab's naming lifecycle (SetSessionTitle → Meaningful=true) and suppress the daemon's AI title derivation (Phase B), leaving the tab stuck on the first prompt. An unnamed session returns "" so Phase B derives a meaningful name instead. Returns "" if the file isn't found or has no session_info.name.

func RegisterSessionTitleResolver

func RegisterSessionTitleResolver(agent string, fn func(sessionID string) string)

RegisterSessionTitleResolver registers a transcript-title resolver for an agent (called from each agent's unified package init). Idempotent; a later registration overrides (useful for tests).

func SessionTitleForAgent

func SessionTitleForAgent(agent, sessionID string) string

SessionTitleForAgent resolves the display title of an agent's session from its own transcript store, generic over the agent id via the registry. Used by the daemon on SessionStart (after an agent-forwarded name, if any) to title a tab that just loaded/resumed a session, so the tab + Live Now match the Projects list instead of showing the shell name. Returns "" when the agent has no resolver registered or the session has no nameable content.

Types

type Known

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

Known is the goroutine-safe set of daemon-owned agent session ids.

func LoadKnown

func LoadKnown() *Known

LoadKnown reads (or initializes) the known-id store from $RMOTE_AGENT_DIR/sessions_known.json. A missing file is not an error — returns an empty store (the backfill then populates it on first run).

func (*Known) BackfillOnce

func (k *Known) BackfillOnce()

BackfillOnce seeds the store with every agent session id currently discoverable on disk, but only on the first run after the known-filter ships (presence of the marker file gates it). This adopts existing history so the Sessions Panel does not go empty when the filter activates. Safe to call at daemon startup; no-op on every subsequent boot.

func (*Known) Has

func (k *Known) Has(id string) bool

Has reports whether id is daemon-owned.

func (*Known) Record

func (k *Known) Record(id string)

Record marks id as daemon-owned. Idempotent; persists only on change so a flood of SessionStart hooks for already-known ids does not churn the file.

func (*Known) Snapshot

func (k *Known) Snapshot() map[string]bool

Snapshot returns a copy of the known set as a bool map (the shape the unified walkers consume). Cheap enough to call once per /api/sessions/ unified request; the list walk dominates cost.

type ListResult

type ListResult struct {
	Sessions      []Session      `json:"sessions"`
	Total         int            `json:"total"`
	Page          int            `json:"page"`
	PerPage       int            `json:"per_page"`
	HasMore       bool           `json:"has_more"`
	ProjectCounts map[string]int `json:"project_counts,omitempty"`
}

ListResult is the response shape for GET /api/sessions/unified. Matches iOS's UnifiedSessionsResponse Codable.

func List

func List(search, project, marker string, openIDs, knownIDs map[string]bool, overrides *Overrides, modelTitles map[string]string) (*ListResult, error)

List walks ~/.claude/projects + ~/.codex/sessions + ~/.grok/sessions and returns the unified list. Search/project filter the results; openIDs marks rows whose agent session is currently live on the daemon (is_open=true). knownIDs gates which rows appear at all — only daemon-owned ids (or live ones) survive, mirroring the macOS server's OverlayStore.known so the panel is not polluted by foreign-CLI transcripts. The result is sorted by mtime desc + capped per-project to 3 entries for the main list (when project=="" and marker!="favorite"), matching the Mac's "show top 3 per project" policy. marker=="favorite" filters to pinned rows only (uncapped, global) — the Mac-side favorite filter; the daemon previously ignored the param and returned everything. Per-project pagination is the caller's job (slice the returned slice).

type MetaCache

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

MetaCache memoizes transcript parses keyed by transcript path. Goroutine-safe.

func LoadMetaCache

func LoadMetaCache() *MetaCache

LoadMetaCache reads sessions_meta.json (missing/unreadable/corrupt file → empty cache, never an error). Always returns a usable (possibly cold) cache.

func (*MetaCache) Counters

func (c *MetaCache) Counters() (hits, misses, parses uint64)

Counters returns (hits, misses, parses) for acceptance/debug inspection.

func (*MetaCache) Evict

func (c *MetaCache) Evict(path string)

Evict drops one entry by path. Used by handleUnifiedDelete when the transcript path is known (Claude branch). Codex/Grok rely on reapMissing.

func (*MetaCache) GC

func (c *MetaCache) GC(maxAge time.Duration)

GC drops entries whose LastActiveUnix is older than maxAge.

func (*MetaCache) PersistNow

func (c *MetaCache) PersistNow()

PersistNow writes the cache atomically (tmp + rename), mirroring known.persist. No-op when nothing changed since the last persist (dirty flag). Called by the background tick + on demand.

type Override

type Override struct {
	Pinned     bool   `json:"pinned,omitempty"`
	CustomName string `json:"custom_name,omitempty"`
}

Override is one session's user-mutable state.

type Overrides

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

Overrides is the in-memory + on-disk store. Goroutine-safe.

func LoadOverrides

func LoadOverrides() (*Overrides, error)

LoadOverrides reads (or initializes) the override store from $RMOTE_AGENT_DIR/sessions_overrides.json. A missing file is not an error — returns an empty store.

func (*Overrides) Get

func (o *Overrides) Get(id string) Override

Get returns the override for a session id (zero value if absent).

func (*Overrides) SetCustomName

func (o *Overrides) SetCustomName(id, name string)

SetCustomName sets/clears the custom name + persists. Empty name clears the field (and if pinned is also false, removes the entry).

func (*Overrides) SetPinned

func (o *Overrides) SetPinned(id string, pinned bool)

SetPinned toggles the pin flag + persists. pinned=false clears the flag (and if custom_name is also empty, removes the entry entirely so the file doesn't accumulate tombstones).

type Session

type Session struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	ProjectPath string   `json:"project_path"`
	LastActive  string   `json:"last_active"` // RFC3339
	Labels      []string `json:"labels"`
	IsPinned    bool     `json:"is_pinned"`
	IsOpen      bool     `json:"is_open"`
	Agent       string   `json:"agent"`
	// Internal-only fields below — never serialized. Used by the server
	// handler to compute per-session stats lazily (only for the returned
	// slice, not every row in the unified list).
	TranscriptPath string `json:"-"`
	SortTs         int64  `json:"-"`
}

Session is one row in the unified list. JSON tags match iOS's UnifiedSession Codable. `Agent` is always "claude" in v1.

func (Session) String

func (s Session) String() string

stringer for debugging — not used in production paths.

type Stats

type Stats struct {
	Turns                    int    `json:"turns"`
	InputTokens              int64  `json:"input_tokens"`
	CacheCreationInputTokens int64  `json:"cache_creation_input_tokens"`
	CacheReadInputTokens     int64  `json:"cache_read_input_tokens"`
	OutputTokens             int64  `json:"output_tokens"`
	TotalTokens              int64  `json:"total_tokens"`
	Messages                 int    `json:"messages"`
	Model                    string `json:"model,omitempty"`
}

Stats is the per-session rollup. JSON tags match iOS's SessionStats Codable field-for-field. `Model` is the most-recently-seen model on an assistant turn (Claude can switch mid-session; we report the last one as canonical).

func ComputeStats

func ComputeStats(path, agent string) *Stats

ComputeStats returns the stats rollup for one session's transcript, using the cached entry when the file's mtime hasn't changed. `agent` selects the transcript format ("claude" / "codex" / "grok"); unknown agents return nil.

`path` is the transcript location — for Claude/Codex it's the .jsonl file; for Grok it's the session directory (we look up summary.json inside).

Jump to

Keyboard shortcuts

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