storage

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrTaskNotFound    = errors.New("task not found")
	ErrAlreadyClaimed  = errors.New("task already claimed by another owner")
	ErrAlreadyResolved = errors.New("task already completed")
	ErrBlocked         = errors.New("task has unresolved blockers")
)

Sentinel errors returned by Claim so callers can branch on cause without string-matching.

Functions

func CompareTaskIDs added in v0.1.1

func CompareTaskIDs(a, b string) int

CompareTaskIDs compares two task ID strings numerically.

func GenerateName

func GenerateName() string

GenerateName returns a random name in adjective-gerund-noun format.

func SortTasksByID added in v0.1.1

func SortTasksByID(tasks []Task)

SortTasksByID sorts tasks by numeric ID ascending.

Types

type CacheBreakInfo added in v0.1.1

type CacheBreakInfo struct {
	PrevCacheReadTokens int     `json:"prev_cache_read_tokens"`
	CurrCacheReadTokens int     `json:"curr_cache_read_tokens"`
	DropAbsolute        int     `json:"drop_absolute"`
	DropFraction        float64 `json:"drop_fraction"`
	// SystemChanged is true when either the frozen prefix or the dynamic
	// tail of the system blocks changed. Kept for backwards compatibility
	// with existing JSONL — prefer the finer-grained fields below.
	SystemChanged  bool   `json:"system_changed,omitempty"`
	FrozenChanged  bool   `json:"frozen_system_changed,omitempty"`
	DynamicChanged bool   `json:"dynamic_system_changed,omitempty"`
	ToolsChanged   bool   `json:"tools_changed,omitempty"`
	Note           string `json:"note,omitempty"`
}

CacheBreakInfo is attached to an LLMCallEntry when the prompt cache hit rate unexpectedly dropped relative to the previous turn. It captures why the cache likely invalidated so the session can be diagnosed after-the- fact without replaying the full request.

type Compaction

type Compaction struct {
	Summary  string            `json:"summary"`
	Messages []json.RawMessage `json:"messages,omitempty"`
}

Compaction stores a compaction summary and optionally kept messages.

type ContextSnapshot

type ContextSnapshot struct {
	Messages        []agentcore.AgentMessage
	Provider        string
	Model           string
	ReasoningEffort string
	PlanSlug        string
	PlanPhase       string
	PlanPreMode     string
	Goal            GoalStateEntry
}

ContextSnapshot is the projected runtime state from a session log.

type Entry

type Entry struct {
	Kind      EntryKind       `json:"kind"`
	ID        string          `json:"id"`
	ParentID  string          `json:"parent_id,omitempty"`
	Timestamp time.Time       `json:"timestamp"`
	Data      json.RawMessage `json:"data"`
}

Entry is a single JSONL line in the session file.

type EntryKind

type EntryKind string

EntryKind identifies the type of a JSONL entry.

const (
	EntryHeader                EntryKind = "header"
	EntryMessage               EntryKind = "message"
	EntryModelChange           EntryKind = "model_change"
	EntryCompaction            EntryKind = "compaction"
	EntryReasoningEffortChange EntryKind = "reasoning_effort_change"
	EntrySessionInfo           EntryKind = "session_info"
	EntryPlanState             EntryKind = "plan_state"
	EntryGoalState             EntryKind = "goal_state"
	EntryLLMCall               EntryKind = "llm_call"
)

type GoalStateEntry added in v0.2.2

type GoalStateEntry struct {
	ID                       string    `json:"id,omitempty"`
	Objective                string    `json:"objective,omitempty"`
	Status                   string    `json:"status"`
	CreatedAt                time.Time `json:"created_at,omitempty"`
	UpdatedAt                time.Time `json:"updated_at,omitempty"`
	CompletedAt              time.Time `json:"completed_at,omitempty"`
	BlockedAt                time.Time `json:"blocked_at,omitempty"`
	BudgetLimitedAt          time.Time `json:"budget_limited_at,omitempty"`
	UsageLimitedAt           time.Time `json:"usage_limited_at,omitempty"`
	Reason                   string    `json:"reason,omitempty"`
	BlockedReason            string    `json:"blocked_reason,omitempty"`
	BlockedCount             int       `json:"blocked_count,omitempty"`
	BlockedAttemptTokenTotal int       `json:"blocked_attempt_token_total,omitempty"`
	BudgetLimitReported      bool      `json:"budget_limit_reported,omitempty"`
	TokenBudget              int       `json:"token_budget,omitempty"`
	TokensUsed               int       `json:"tokens_used,omitempty"`
	TokenTotalAtLastAccount  int       `json:"token_total_at_last_account,omitempty"`
}

GoalStateEntry records the explicit /goal state for session resume.

type Header struct {
	Version   int       `json:"version"`
	SessionID string    `json:"session_id"`
	Name      string    `json:"name,omitempty"`
	Cwd       string    `json:"cwd"`
	Created   time.Time `json:"created"`
}

Header is the first line of a session file.

type History

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

History provides append-only input history with per-project filtering. The backing file (~/.codebot/history.jsonl) is shared across all projects; only entries matching the current project are surfaced.

func NewHistory

func NewHistory(path, project, sessionID string) *History

NewHistory loads history from path, filtering by project.

func (*History) Add

func (h *History) Add(text string)

Add appends text to history. Duplicates are moved to the front.

func (*History) Get

func (h *History) Get(index int) string

Get returns the history entry at index (0 = most recent).

func (*History) Len

func (h *History) Len() int

Len returns the number of history entries for the current project.

func (*History) SetSessionID

func (h *History) SetSessionID(id string)

SetSessionID updates the session ID for subsequent entries (e.g. after session switch).

type HistoryEntry

type HistoryEntry struct {
	Display        string         `json:"display"`
	PastedContents map[string]any `json:"pastedContents"`
	Timestamp      int64          `json:"timestamp"`
	Project        string         `json:"project"`
	SessionID      string         `json:"sessionId,omitempty"`
}

HistoryEntry is one line in the JSONL history file.

type LLMCallEntry added in v0.1.1

type LLMCallEntry struct {
	Provider            string          `json:"provider"`
	Model               string          `json:"model"`
	InputTokens         int             `json:"input_tokens"`
	OutputTokens        int             `json:"output_tokens"`
	CacheReadTokens     int             `json:"cache_read_tokens,omitempty"`
	CacheCreationTokens int             `json:"cache_creation_tokens,omitempty"`
	TotalTokens         int             `json:"total_tokens,omitempty"`
	LatencyMs           int64           `json:"latency_ms,omitempty"`
	StopReason          string          `json:"stop_reason,omitempty"`
	ReasoningEffort     string          `json:"reasoning_effort,omitempty"`
	CacheBreak          *CacheBreakInfo `json:"cache_break,omitempty"`
}

LLMCallEntry is a per-turn observability record for a single LLM response. Emitted once per assistant message_end, independent of the message itself so that downstream can diagnose cache hits, latency, and provider without re-parsing the message payload.

type Manager

type Manager struct {
	Dir string
}

Manager manages session files in a directory.

func NewManager

func NewManager(dir string) *Manager

NewManager creates a Manager for the given sessions directory.

func (*Manager) Create

func (m *Manager) Create(cwd string) (*Store, error)

Create creates a new session.

func (*Manager) List

func (m *Manager) List() ([]SessionInfo, error)

List returns all sessions sorted by updated time (newest first).

func (*Manager) MostRecent

func (m *Manager) MostRecent() (*SessionInfo, error)

MostRecent returns the most recently updated session.

func (*Manager) Open

func (m *Manager) Open(id string) (*Store, error)

Open opens an existing session by ID.

func (*Manager) OpenPath

func (m *Manager) OpenPath(path string) (*Store, error)

OpenPath opens a session by file path.

type ModelChange

type ModelChange struct {
	Provider string `json:"provider"`
	Model    string `json:"model"`
}

ModelChange records a model switch event.

type PlanFile

type PlanFile struct {
	Name    string // filename without .md
	ModTime int64  // unix timestamp
}

PlanFile represents a plan file entry for listing.

type PlanStateEntry added in v0.1.0

type PlanStateEntry struct {
	Phase   string `json:"phase"`
	Slug    string `json:"slug,omitempty"`
	PreMode string `json:"pre_mode,omitempty"`
}

PlanStateEntry records a plan-mode phase transition.

type PlanStore

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

PlanStore manages plan markdown files in a single directory.

func NewPlanStore

func NewPlanStore(dir string) *PlanStore

NewPlanStore creates a PlanStore and ensures the directory exists.

func (*PlanStore) Delete

func (s *PlanStore) Delete(name string) error

Delete removes a plan file.

func (*PlanStore) Dir added in v0.1.3

func (s *PlanStore) Dir() string

Dir returns the directory plans are stored in. Used by the TUI to detect whether a write/edit target is a plan file (hidden rendering).

func (*PlanStore) List

func (s *PlanStore) List() ([]PlanFile, error)

List returns all plan files sorted by modification time (newest first).

func (*PlanStore) Load

func (s *PlanStore) Load(name string) (string, error)

Load reads a plan file by name. Returns "", nil if not found.

func (*PlanStore) Path added in v0.1.0

func (s *PlanStore) Path(name string) string

func (*PlanStore) Save

func (s *PlanStore) Save(name, content string) error

Save writes plan content as a markdown file.

type ReasoningEffortChange added in v0.3.0

type ReasoningEffortChange struct {
	Level string `json:"level"`
}

ReasoningEffortChange records a reasoning effort switch event.

type Roster added in v0.2.0

type Roster struct {
	Team        string         `json:"team"`
	Description string         `json:"description,omitempty"`
	Members     []RosterMember `json:"members,omitempty"`
}

Roster is the persisted team snapshot: the active team's identity plus every live teammate. It mirrors the in-memory team.Registry roster so a restarted session can rebuild the team and re-spawn its members.

type RosterMember added in v0.2.0

type RosterMember struct {
	Name          string `json:"name"`
	AgentType     string `json:"agent_type"`
	Color         string `json:"color,omitempty"`
	InitialPrompt string `json:"initial_prompt,omitempty"`
	Description   string `json:"description,omitempty"`
	Depth         int    `json:"depth,omitempty"`
	Kind          string `json:"kind,omitempty"`
}

RosterMember is the minimal, faithful record needed to re-spawn one teammate after a restart. It deliberately stores only spawn-time facts that are NOT recoverable from the agent definition registry:

  • Name is the unique routing identifier (post de-duplication), distinct from AgentType when the leader spawned two of the same kind.
  • AgentType is the definition key (subagent.Config.Name) the harness rebuilds the full Config from on resume — model, system prompt, tools and context manager all come back via that lookup, so they are not duplicated here.
  • Color / InitialPrompt / Description / Depth are per-spawn overrides the definition does not carry.
  • Kind tags the agent's spawn mode. Today every team-spawned agent is a teammate; the field exists so restore/UI can branch once background or one-shot agents also land in the roster. Empty ⇒ treat as teammate.

type RosterStore added in v0.2.0

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

RosterStore persists a single team Roster to <dir>/roster.json with atomic writes. Like TaskStore it is safe for in-memory-only use: with no dir set every mutation is kept in memory and nothing touches disk. The directory is created lazily on the first write so sessions that never form a team leave nothing behind.

func NewRosterStore added in v0.2.0

func NewRosterStore() *RosterStore

NewRosterStore returns an empty in-memory store. Call SetDir to enable persistence (and load any prior roster).

func (*RosterStore) Clear added in v0.2.0

func (s *RosterStore) Clear()

Clear drops the team and all members, removing roster.json from disk. Used when the leader deletes the team (team_dismiss of the whole team).

func (*RosterStore) RemoveMember added in v0.2.0

func (s *RosterStore) RemoveMember(name string)

RemoveMember drops the member with the given Name (e.g. on team_dismiss) so a restart does not resurrect a teammate the leader deliberately retired. No-op when the name is absent.

func (*RosterStore) SetDir added in v0.2.0

func (s *RosterStore) SetDir(dir string) error

SetDir enables file persistence and loads an existing roster.json if present. A missing dir or file is not an error — it just means no prior team.

func (*RosterStore) SetTeam added in v0.2.0

func (s *RosterStore) SetTeam(name, description string)

SetTeam records the active team's name and description, replacing any prior team. Members are preserved (rename/describe must not drop the roster); use Clear to drop a team entirely.

func (*RosterStore) Snapshot added in v0.2.0

func (s *RosterStore) Snapshot() Roster

Snapshot returns a deep copy of the current roster for resume. The Members slice is freshly allocated so callers can iterate without holding the lock.

func (*RosterStore) UpsertMember added in v0.2.0

func (s *RosterStore) UpsertMember(m RosterMember)

UpsertMember adds m, or replaces the existing member with the same Name. Matching by Name (the routing key) means a re-spawn under an existing name updates rather than duplicates.

type SessionInfo

type SessionInfo struct {
	ID           string
	Name         string
	Path         string
	Cwd          string
	Created      time.Time
	Updated      time.Time
	MessageCount int
	FirstMessage string // first user message, truncated to 80 chars
}

SessionInfo is a summary of a session for listing.

type Store

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

Store manages a single session JSONL file.

func (*Store) AppendCompaction

func (s *Store) AppendCompaction(summary string, keptMessages []json.RawMessage) error

AppendCompaction records a compaction event with summary and optional kept messages.

func (*Store) AppendGoalState added in v0.2.2

func (s *Store) AppendGoalState(entry GoalStateEntry) error

func (*Store) AppendLLMCall added in v0.1.1

func (s *Store) AppendLLMCall(entry LLMCallEntry) error

AppendLLMCall records a single LLM response's observability metadata. This is written alongside the assistant message itself, so the message payload stays minimal while diagnostics (cache/latency/provider) stay queryable without replaying the whole message.

func (*Store) AppendMessage

func (s *Store) AppendMessage(msg agentcore.Message) error

AppendMessage serializes and appends an agentcore.Message.

Thinking blocks are truncated to maxStoredThinkingRunes before write to keep session files compact. Truncation is storage-only — the in-memory message retains full thinking for this turn, since agentcore may still need it during the same agent_end lifecycle.

func (*Store) AppendModelChange

func (s *Store) AppendModelChange(provider, model string) error

AppendModelChange records a model switch.

func (*Store) AppendPlanState added in v0.1.0

func (s *Store) AppendPlanState(phase, slug, preMode string) error

func (*Store) AppendReasoningEffortChange added in v0.3.0

func (s *Store) AppendReasoningEffortChange(level string) error

AppendReasoningEffortChange records a reasoning effort switch.

func (*Store) BuildSnapshot

func (s *Store) BuildSnapshot() (ContextSnapshot, error)

BuildSnapshot reconstructs runtime state by walking the tree from the current leaf.

func (*Store) Close

func (s *Store) Close() error

Close closes the session file.

func (*Store) Header

func (s *Store) Header() Header

Header returns the session header.

func (*Store) Path

func (s *Store) Path() string

Path returns the session file path.

func (*Store) SetName

func (s *Store) SetName(name string) error

SetName updates the session display name by appending a session_info entry.

type Task added in v0.1.1

type Task struct {
	ID          string         `json:"id"`
	Subject     string         `json:"subject"`
	Description string         `json:"description,omitempty"`
	ActiveForm  string         `json:"activeForm,omitempty"`
	Status      TaskStatus     `json:"status"`
	Owner       string         `json:"owner,omitempty"`
	Blocks      []string       `json:"blocks"`
	BlockedBy   []string       `json:"blockedBy"`
	Metadata    map[string]any `json:"metadata,omitempty"`
	// CompletedAt is set when the task transitions into TaskCompleted and
	// cleared if it ever reverts to a non-completed state. Pointer + omitempty
	// keeps older persisted JSON files (without this field) loading cleanly.
	// The TUI uses it to keep recently-completed tasks pinned to the top of
	// the truncated task tree for ~30s before they sink to the bottom.
	CompletedAt *time.Time `json:"completedAt,omitempty"`
}

Task is a single tracked work item.

type TaskNotifyFn added in v0.1.1

type TaskNotifyFn func(TaskSnapshot)

TaskNotifyFn is called after each store mutation with the latest snapshot.

type TaskSnapshot added in v0.1.1

type TaskSnapshot struct {
	Items      []Task
	Pending    int
	InProgress int
	Completed  int
	Total      int
}

TaskSnapshot is a read-only snapshot of all tasks sent to the TUI.

type TaskStatus added in v0.1.1

type TaskStatus string

TaskStatus represents the lifecycle state of a task.

const (
	TaskPending    TaskStatus = "pending"
	TaskInProgress TaskStatus = "in_progress"
	TaskCompleted  TaskStatus = "completed"
)

type TaskStore added in v0.1.1

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

TaskStore is a thread-safe task store with optional file persistence.

func NewTaskStore added in v0.1.1

func NewTaskStore() *TaskStore

NewTaskStore creates an empty store.

func (*TaskStore) Claim added in v0.2.0

func (s *TaskStore) Claim(id, owner string) (*Task, error)

Claim atomically assigns owner to task id, but only when the task is in a claimable state: it exists, has no current owner (or already equals owner — idempotent retry), is not completed, and all of its blockedBy dependencies are completed. Returns the updated task on success or a sentinel error explaining the rejection.

This is the CAS primitive that makes work-stealing safe: when two idle teammates race for the same unowned task, only the first Claim succeeds; the loser sees ErrAlreadyClaimed and falls through to find another task. The in-memory mutex is the only lock — codebot is single-process.

Deliberately NOT implemented: a "busy-check" variant that rejects when the claimant already owns another open task. The pull path is strictly sequential per agent (FindClaimable → Claim → run turn → return → pull again), so one-in-flight-per-agent holds by construction; the only case such a check would catch is a model marking two tasks in_progress inside the same turn — a degenerate prompt-shape problem, not a race.

func (*TaskStore) Create added in v0.1.1

func (s *TaskStore) Create(subject, description, activeForm string, metadata map[string]any) *Task

Create adds a new task and returns a copy.

func (*TaskStore) Delete added in v0.1.1

func (s *TaskStore) Delete(id string) bool

Delete removes a task and any persisted file.

func (*TaskStore) FindClaimable added in v0.2.0

func (s *TaskStore) FindClaimable() *Task

FindClaimable returns a copy of the next claimable task by ascending ID, or nil when nothing is available. Claimable = owner empty, status != completed, and every blockedBy reference points to a completed task. Pure read; safe for the dispatcher hot loop.

func (*TaskStore) Get added in v0.1.1

func (s *TaskStore) Get(id string) (*Task, bool)

Get returns a copy of the task or false if not found.

func (*TaskStore) List added in v0.1.1

func (s *TaskStore) List() []Task

List returns copies of all tasks sorted by ID.

func (*TaskStore) Reset added in v0.1.1

func (s *TaskStore) Reset() error

Reset clears all tasks from memory and persistence while preserving the monotonic task ID sequence for future task creation.

func (*TaskStore) SetDir added in v0.1.1

func (s *TaskStore) SetDir(dir string) error

SetDir enables file persistence. The directory is created lazily on the first write (persistLocked / writeHighWaterMarkLocked) — sessions that never create a task leave nothing behind on disk.

func (*TaskStore) SetNotifyFn added in v0.1.1

func (s *TaskStore) SetNotifyFn(fn TaskNotifyFn)

SetNotifyFn registers a callback invoked after every mutation.

func (*TaskStore) Snapshot added in v0.1.1

func (s *TaskStore) Snapshot() TaskSnapshot

Snapshot returns the current read-only snapshot.

func (*TaskStore) Update added in v0.1.1

func (s *TaskStore) Update(id string, opts TaskUpdateOpts) (*Task, error)

Update modifies a task and returns the updated copy.

type TaskUpdateOpts added in v0.1.1

type TaskUpdateOpts struct {
	Status       *TaskStatus
	Subject      *string
	Description  *string
	ActiveForm   *string
	Owner        *string
	Metadata     map[string]any // merged; nil-valued keys are deleted
	AddBlocks    []string
	AddBlockedBy []string
}

TaskUpdateOpts describes optional fields to update.

type TranscriptStore added in v0.2.0

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

TranscriptStore persists each teammate's conversation as an append-only JSONL file at <dir>/<agentName>.jsonl — one marshaled agentcore.Message per line, in conversation order. It is the durable counterpart to the in-memory team.EventHub: the hub is lossy (drop-oldest, for live UI), whereas this captures every turn losslessly so a restarted session can rebuild a teammate's full context.

Append is driven from the teammate's turn executor (the same messages the team runner stitches into its history), so persistence and the live loop see identical bytes. Load + RepairMessageSequence reconstruct a valid sequence even when a crash left a half-written tool call at the tail.

func NewTranscriptStore added in v0.2.0

func NewTranscriptStore(dir string) *TranscriptStore

NewTranscriptStore returns a store rooted at dir (typically config.TeamDir(sessionID)/transcripts). A "" dir disables persistence — every method becomes a no-op — so callers need not special-case it.

func (*TranscriptStore) Append added in v0.2.0

func (s *TranscriptStore) Append(agent string, msgs []agentcore.AgentMessage) error

Append writes each message as one JSON line, creating the dir/file on first use. Append-only: prior turns are never rewritten, so a resumed teammate's new turns extend the existing transcript rather than duplicating loaded history. Safe for concurrent calls across different agents (one mutex serialises the rare per-turn writes).

func (*TranscriptStore) Load added in v0.2.0

func (s *TranscriptStore) Load(agent string) ([]agentcore.AgentMessage, error)

Load reads <agent>.jsonl and returns the repaired message sequence ready to seed a resumed teammate via team.SpawnConfig.History. A missing file yields (nil, nil). An invalid, unterminated final line is ignored as a crash-torn append; malformed complete lines are reported.

Jump to

Keyboard shortcuts

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