activity

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package activity is the append-only record of what an agent actually did.

Everything else in this system is something a model chose to tell us. A memory is stored because the agent called `remember`; a checkpoint exists because it called `checkpoint`. That works right up until the model does not call them, and the failure is silent — a session that decided three things and wrote none of them down looks, from the vault, exactly like a session that never happened.

This is the other half. It records events the agent does not get a vote on: the prompts a person typed, the tools that ran, the turns that ended. The host reports them through hooks whether or not the model would have thought to mention them, which makes this the one part of the record that cannot be forgotten.

Three deliberate choices:

JSONL on disk, one file per month. Not SQLite. The log is meant to be read by things that are not us — grep, jq, a script, the user at three in the morning wondering what happened. A database would be faster to query and would make that impossible, and "your history is a file you own" stops being true the moment the only reader is our binary. Monthly files keep `activity/2026-09.jsonl` a name a person can guess and keep any single file bounded.

Append-only, never rewritten. A log that edits itself is not evidence.

No model, ever. Recording is arithmetic and string handling. A summariser here would turn a factual record into a plausible one, and cost a hook budget it does not have.

Index

Constants

View Source
const (
	KindPrompt  = "prompt"   // a person typed something
	KindTool    = "tool"     // a tool ran and returned
	KindTurnEnd = "turn-end" // the agent finished a turn
	KindNotice  = "notice"   // the host wants the user's attention
	KindStart   = "session-start"
	KindEnd     = "session-end"
	KindBlocked = "permission" // the agent asked to do something and is waiting
	KindWarn    = "warning"    // something the hook path could not report any louder
)

Event kinds. Plain strings, so a host that grows a new event type does not need a migration here — an unknown kind still records, still greps, and still prints. The set below is what Claude Code's hooks can currently tell us.

View Source
const Dir = "activity"

Dir is the vault subdirectory holding the log.

Variables

This section is empty.

Functions

func Append

func Append(vault string, e Event) error

Append writes one event. It creates the month's file if needed and never rewrites what is already there.

Errors are returned rather than swallowed, but callers on a hook path should treat them as advisory: a log that fails to write must not break the session it is recording. The hook decides that; this function only reports.

func Projects

func Projects(vault string) ([]string, error)

Projects lists the projects that appear in the log, busiest first. This is what makes the log browsable without knowing what to ask for.

Types

type Event

type Event struct {
	TS      int64  `json:"ts"`
	Kind    string `json:"kind"`
	Project string `json:"project,omitempty"`
	Session string `json:"session,omitempty"`
	// Agent is the host that reported the event ("claude-code"), not the model.
	Agent string `json:"agent,omitempty"`
	// Tool is the tool's name for KindTool, empty otherwise.
	Tool string `json:"tool,omitempty"`
	// Summary is one line a human can read without expanding anything. It is
	// the whole point of the row; a log whose rows need decoding is a log
	// nobody scans.
	Summary string `json:"summary,omitempty"`
	// Extra carries whatever the host sent that we did not model. Kept so the
	// record stays lossless even where our schema is not.
	Extra map[string]any `json:"extra,omitempty"`
}

Event is one line of the log.

The fields are small and fixed on purpose. This file is appended to from a hook on the critical path of every tool call, so it holds what is cheap to know and true without interpretation — and puts anything host-specific in Extra rather than growing a column per host.

func FromHook

func FromHook(event string, raw []byte, project string) (Event, error)

FromHook builds an Event from a host hook payload. event is the host's event name; raw is the JSON it wrote to the hook's stdin. project is used when the payload does not carry enough to work one out.

func Read

func Read(vault string, q Query) ([]Event, error)

Read returns matching events, newest first.

A malformed line is skipped rather than fatal. The log is appended to by hooks running in whatever shell the user has, and one truncated line from a machine that lost power should cost that line, not the month.

type Query

type Query struct {
	Project string
	Kind    string
	Tool    string
	Since   time.Time
	Until   time.Time
	// Limit caps the result, keeping the *newest* matches. A limit applied
	// while scanning would keep the oldest, which is the opposite of what
	// anyone asking for "the last 20" means.
	Limit int
}

Query narrows the log. A zero value matches everything.

Jump to

Keyboard shortcuts

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