Documentation
¶
Overview ¶
Package opencode implements the event-level adapter for the opencode CLI.
opencode stores per-message usage both in a SQLite database and as JSON files. Under each data directory we read BOTH:
- SQLite "opencode.db" (or the first "opencode-<token>.db") — table `message(id, session_id, data)` where `data` is the message JSON.
- JSON files under "storage/message/**/*.json" (the same shape).
Both carry the same per-message `data` payload:
{id, sessionID, providerID, modelID, time:{created:<ms>},
tokens:{input, output, reasoning, cache:{read, write}, total},
cost, path:{cwd, root}}
Token mapping (opencode reports cache read/write as separate buckets, like Anthropic): Input=tokens.input, Output=tokens.output, CacheCreation=tokens.cache.write, CacheRead=tokens.cache.read, Reasoning=tokens.reasoning, and Total is reconciled against tokens.total via tokenutil.ApplyTotalFallback.
Reasoning is normalized to ADDITIVE output. Current writers subtract it from output; older writers retained it inside output. A positive total that exactly accounts for input/output/cache identifies that overlap (see buildEvent).
The persisted dedup key is "opencode|<message id>", so the SQLite row and the JSON file for the same message collapse to one stored event (DB is discovered first, so it wins on INSERT OR IGNORE).
CRITICAL: strictly read-only. JSON files are opened O_RDONLY; the database is opened with a read-only DSN (mode=ro plus query_only(1)) — never immutable=1, because opencode writes this database live and keeps a large WAL an immutable reader cannot see (see collectDB). Nothing under the agent's directories is created, locked, or modified.
Index ¶
- Constants
- func New() adapter.Adapter
- type Adapter
- func (Adapter) Capabilities() model.ToolCapability
- func (a Adapter) Collect(ctx context.Context, src adapter.Source) (adapter.Observation, error)
- func (a Adapter) CollectIncremental(ctx context.Context, src adapter.Source, cp *model.SourceCheckpoint) (adapter.Observation, error)
- func (a Adapter) Discover(ctx context.Context, cfg adapter.DiscoverConfig) ([]adapter.Source, error)
- func (Adapter) DisplayName() string
- func (Adapter) ID() string
Constants ¶
const ( // DataDirEnv names the environment variable that moves the opencode data // directory, and with it every database this adapter reads. DataDirEnv = "OPENCODE_DATA_DIR" )
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Adapter ¶
type Adapter struct{}
Adapter reads opencode CLI message usage. Read-only.
func (Adapter) Capabilities ¶
func (Adapter) Capabilities() model.ToolCapability
Capabilities declares what this project can say about opencode.
Cost is COMPUTED: nothing here calls SetCost. Activity is an EXACT join — collectActivity joins part.message_id to message.id, the very id the usage dedup key is already built from.
func (Adapter) CollectIncremental ¶
func (a Adapter) CollectIncremental(ctx context.Context, src adapter.Source, cp *model.SourceCheckpoint) (adapter.Observation, error)
CollectIncremental scans new database rows and retries pending modern messages. Legacy databases retain their rowid cursor; JSON trees are always read in full.
func (Adapter) Discover ¶
func (a Adapter) Discover(ctx context.Context, cfg adapter.DiscoverConfig) ([]adapter.Source, error)
Discover locates, per data dir, the SQLite database (if any) and the JSON message tree (if any). The database is discovered FIRST so that, on a dedup collision with the JSON copy, the DB row wins (INSERT OR IGNORE).
func (Adapter) DisplayName ¶
DisplayName returns the human-friendly name.