Appearance
Database schema
The warehouse is one SQLite file, and logbook sql runs read-only queries over it. These are its tables, as logbook sql --help prints them.
sh
logbook sql "SELECT bare_name, COUNT(*) AS calls FROM tool_call GROUP BY bare_name ORDER BY calls DESC LIMIT 10"Times are epoch milliseconds, UTC; NULL means the source did not record it. Booleans are 0 or 1. A session id is <harness id>:<source id>; message, tool call and event ids are <session id>/<source id>; a reaction id is <message id>#<n>.
harness
One row per adapter any sync registered.
| Column | Notes |
|---|---|
id | the adapter id, the prefix of its session ids |
name | |
default_agent | shown when a session names no agent |
filter_alias | the word after harness: in the conversation filter |
is_found | 0|1 |
checked_at | |
location | where the data was found, or where the adapter looked |
location_variables | JSON array of the variables that move it |
version_seen | the newest harness version the last sync saw |
notice | |
problem |
source_state
Per source unit, the fingerprint and parser version that decide whether a sync imports it again.
| Column | Notes |
|---|---|
harness | |
locator | |
fingerprint | |
parser_version | |
imported_at |
sync_run
One row per sync, the newest 100.
| Column | Notes |
|---|---|
id | |
started_at | |
ended_at | |
outcome | ok|partial|failed|stopped |
error |
session
| Column | Notes |
|---|---|
id | |
harness | |
source_id | the id the harness shows |
origin | interactive|scripted|subagent (scripted: a program started it) |
is_scripted | 0|1 (the source shows a program started it) |
project_dir | |
title | |
agent | |
spawned_by_session_id | |
spawned_by_tool_call_id | |
started_at | |
ended_at |
message
| Column | Notes |
|---|---|
id | |
session_id | |
seq | orders a conversation; times can overlap |
actor | user|assistant|tool|harness (harness: text the harness injected or wrote in the user's name) |
source_role | the role as the harness recorded it |
created_at | |
completed_at | |
requested_at | when the model request behind an assistant message started |
model | |
agent | |
git_branch | |
tokens_input | |
tokens_output | |
tokens_reasoning | |
tokens_cache_read | |
tokens_cache_write | tokens per model request |
reported_cost | as the provider reported it; 0 under a subscription is not a price |
part
| Column | Notes |
|---|---|
rowid | |
message_id | |
session_id | |
idx | the part's place in its message |
kind | text|reasoning|tool_call|tool_result|compaction |
text | every message's text, whole |
tool_call_id |
part_fts
FTS5 over part.text, join on part_fts.rowid = part.rowid; it does not stem, and a quoted phrase matches as a phrase.
| Column | Notes |
|---|---|
text |
tool_call
| Column | Notes |
|---|---|
id | |
session_id | |
message_id | |
name | as the model called it |
bare_name | without any server prefix |
server | the MCP server or plugin, NULL for a built-in tool |
family | shell|read|edit|search|subagent|dispatch|web|todo|skill|tool-search|question|wait|mcp:<server>|other (dispatch: a tool that starts another agent as its own session) |
input_json | |
status | completed|error|pending |
child_session_id | |
started_at | |
ended_at |
event
Data_json of the kinds readers parse, only when the harness records them: tools-offered {added, removed, surfaced, pendingServers, needsAuthServers, failedServers [{name, error}]}; tools-loaded {tools [{name, chars}]}; skill-loaded {name, chars, toolCallId} (toolCallId NULL when no call loaded it).
| Column | Notes |
|---|---|
id | |
session_id | |
kind | compaction|model-switched|agent-switched|idle|error|tools-offered|tools-loaded|skill-loaded|interrupted|tool-rejected|unknown (interrupted: the human stopped the agent; tool-rejected: the human refused a tool call) |
at | |
data_json |
link
Which session started which.
| Column | Notes |
|---|---|
parent_session_id | |
parent_tool_call_id | |
child_session_id | |
kind | subagent|dispatch |
confidence | |
evidence |
session_command
| Column | Notes |
|---|---|
session_id | |
message_id | |
at | |
command | without its slash |
source | typed|template |
has_file | 0|1 (1 for a user or project command file, 0 for a harness built-in) |
turn
Per turn, rebuilt on every sync: wall time is ended_at - started_at, active time model_ms + tool_ms, and parent_turn_id and parent_tool_call_id say where it nests.
| Column | Notes |
|---|---|
session_id | |
message_id | its first message, join message.id |
seq | |
is_prompt | 0|1 |
requests | |
tool_calls | |
started_at | |
ended_at | |
model_ms | |
tool_ms | |
idle_ms | |
human_wait_ms | the part of idle_ms a tool waited for the human to answer |
parent_turn_id | |
parent_tool_call_id |
label
One row per field; take a model field from its newest row.
| Column | Notes |
|---|---|
record_type | tool_call|session|message|reaction |
record_id | join tool_call.id, session.id or message.id; a reaction is <message id>#<n> |
labeller | rules, rebuilt on every sync, or the model id that gave it, kept |
version | |
name | |
value | |
labelled_at |
label_run
One row per model labelling run.
| Column | Notes |
|---|---|
id | |
pid | |
started_at | |
ended_at | |
outcome | ok|stopped|limit|unreachable|failed |
error | |
model |
label_run_task
One row per task of a labelling run: done of planned records.
| Column | Notes |
|---|---|
run_id | |
task | |
version | |
planned | |
done |
forgotten
The ids of sessions the user asked Log Book to forget.
| Column | Notes |
|---|---|
session_id | |
forgotten_at |