hermes

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package hermes reads the sessions of Hermes Agent (NousResearch/hermes-agent), the Python agent behind the hermes CLI, its TUI and its messaging gateway. Hermes keeps every session of a profile in one SQLite database, state.db, under its home directory (~/.hermes/state.db, and ~/.hermes/profiles/<name>/state.db for each further profile): the sessions table, a row per session, the messages table, a row per message in OpenAI's chat-completions shape, and the tables beside them a conversion reads, system_prompts, session_model_usage and async_delegations.

It imports the standard library alone, so it does not open a database itself: the hermes/sqlite package opens one and yields its rows as this package's types, through the Store interface. Rows are read by column name, since Hermes adds columns to its tables as it needs them and an older database lacks the newer ones; a column this package does not know is kept with the row. The schema version the database records is read too, and the reading rules that depend on it are this package's.

A row that cannot be read is a Stray, never an error. The package does not interpret what the model saw: Classify tells a child session's relation to its parent as Hermes does, and the convert subpackage rebuilds each session's context; docs/formats/hermes.md is the mapping. Not read here: the ShareGPT trajectories, the trace export, the session JSON and JSONL files Hermes falls back to when the database is unavailable, and the export of hermes sessions export.

Index

Constants

View Source
const (
	RoleUser      = "user"
	RoleAssistant = "assistant"
	RoleTool      = "tool"
	RoleSystem    = "system"
	// RoleSessionMeta is the gateway's first-turn marker, transcript
	// bookkeeping that no model reads.
	RoleSessionMeta = "session_meta"
)

Message roles Hermes writes.

View Source
const (
	MarkerBranch   = "_branched_from"
	MarkerDelegate = "_delegate_from"
	MarkerReset    = "_reset_from"
)

Model config markers that tie a child session to its parent.

View Source
const ContentPrefix = "\x00json:"

ContentPrefix marks content Hermes stored as JSON, multimodal parts or a dict, rather than text: SessionDB._CONTENT_JSON_PREFIX.

View Source
const DatabaseName = "state.db"

DatabaseName is the name of Hermes' database in its home directory and in each profile's.

View Source
const DirProfiles = "profiles"

DirProfiles is the directory of a home's further profiles, each a directory holding its own state.db.

View Source
const SchemaVersion = 31

SchemaVersion is the newest schema version this package was written against: hermes_state_common.SCHEMA_VERSION at hermes-agent 848fc0ad8b.

Variables

View Source
var ResetEndReasons = map[string]bool{
	"session_reset": true, "session_switch": true, "idle": true, "daily": true, "suspended": true, "resume_pending_expired": true,
}

ResetEndReasons are the end reasons of a parent that a legacy reset child follows: hermes_state_common._RESET_END_REASONS.

Functions

func Databases

func Databases(dir string) []string

Databases lists the databases a home holds: its own state.db and each profile's, ~/.hermes/profiles/<name>/state.db, in name order.

func Epoch

func Epoch(f float64) (time.Time, bool)

Epoch is a Hermes timestamp, REAL seconds since the epoch, as a time; false outside the window Hermes itself accepts (hermes_cli.timefmt's EPOCH_MIN and EPOCH_MAX, 0 to 4.2e9), where a damaged or foreign cell lies.

func FindSessions

func FindSessions(root string) ([]string, error)

FindSessions lists the Hermes databases under root: each state.db, a home's and each of its profiles'. A file root is returned as it is. A database is converted whole: Hermes keeps every session of a profile in one. Directories that cannot be read are passed over, and symbolic links are not followed.

func IsHome

func IsHome(dir string) bool

IsHome reports whether dir is a Hermes home or profile directory: it holds state.db.

func IsNull

func IsNull(raw json.RawMessage) bool

IsNull reports whether a raw value is absent or null.

func ResultAliases

func ResultAliases(id string) []string

ResultAliases are the spellings a tool row's tool_call_id names a call by (tool_result_id_variants).

func StoreOf

func StoreOf(path string) (string, bool)

StoreOf returns the database a path names: the path itself when it is a file, the state.db of a home or profile directory, else false.

Types

type Column

type Column struct {
	Name  string
	Value any
}

Column is one column of a database row: its name and its value as database/sql scans it, an int64, float64, string, []byte or nil.

type Columns

type Columns []Column

Columns is a row of a table, in the table's column order.

func (Columns) Flag

func (cs Columns) Flag(name string, def bool) bool

Flag returns an integer column as a flag: true when it is non-zero, and def when the column is absent or null.

func (Columns) Float

func (cs Columns) Float(name string) (float64, bool)

Float returns a numeric column. Text that spells a number is not one: SQLite would store a REAL value as such.

func (Columns) Get

func (cs Columns) Get(name string) (any, bool)

Get returns a column's value; false when the row has no such column.

func (Columns) Has

func (cs Columns) Has(name string) bool

Has reports whether the row has a column, null or not.

func (Columns) Int

func (cs Columns) Int(name string) (int64, bool)

Int returns an integer column.

func (Columns) JSON

func (cs Columns) JSON() json.RawMessage

JSON writes the row as one JSON object, a member per column in the table's order: a JSON text column as the value it holds when it holds one, text as a string, a number as a number, null as null, and a blob as base64 text.

func (Columns) String

func (cs Columns) String(name string) string

String returns a text column; empty when it is absent, null or not text.

func (Columns) Text

func (cs Columns) Text(name string) (string, bool)

Text returns a text column and whether it is text, not null and not absent: Hermes tells an empty string from NULL in some columns.

func (Columns) Without

func (cs Columns) Without(names ...string) Columns

Without returns the row without the columns named.

type Content

type Content struct {
	// Text is the content when it is text.
	Text string
	// Parts holds structured content, each part as written; a dict is
	// one part.
	Parts []json.RawMessage
	// Structured says the content is Parts; Null that the column is
	// null.
	Structured, Null bool
}

Content is a message's content as Hermes stores it: text, or JSON behind ContentPrefix, multimodal parts or a dict.

func DecodeContent

func DecodeContent(v any, present bool) (c Content, ok bool)

DecodeContent reads a content column as SessionDB._decode_content does: text behind ContentPrefix is JSON, and a reserved prefix holding no valid JSON stays the text it is. ok is false for such text.

func (Content) String

func (c Content) String() string

String is the content's text: the text, or the text parts joined.

type Database

type Database interface {
	Store
	Close() error
}

Database is a store read from a database file, open until closed.

type Delegation

type Delegation struct {
	ID              string
	ParentSessionID string
	OriginSessionID string
	State           string
	Cols            Columns
}

Delegation is the parent session of a background delegation, from a row of async_delegations.

type Function

type Function struct {
	Name      string          `json:"name"`
	Arguments json.RawMessage `json:"arguments"`

	Members
}

Function is a call's function: its name and arguments, a JSON string as OpenAI writes them or an object as some providers do.

func (Function) MarshalJSON

func (f Function) MarshalJSON() ([]byte, error)

MarshalJSON writes the function back as it was read.

func (*Function) UnmarshalJSON

func (f *Function) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the function, keeping undeclared members.

type Members

type Members = jsonline.Members

Members is embedded in every JSON object this package decodes. It keeps the members the Go type does not declare, so an object decoded and encoded again is the same JSON value.

type Memory

type Memory struct {
	StoreName   string
	Schema      int
	SessionRows []*Session
	Strays      []*Stray
	MessagesOf  map[string][]*Message
	Prompts     map[string]string
	Usage       map[string][]Columns
	Delegated   []*Delegation
	// Loose are rows of messages whose session is not in
	// SessionRows.
	Loose []*Message
}

Memory is a store held in memory, for tests and fuzzing: a database's rows, decoded.

func (*Memory) Delegations

func (m *Memory) Delegations() ([]*Delegation, error)

Delegations implements Store.

func (*Memory) Messages

func (m *Memory) Messages(id string) ([]*Message, error)

Messages implements Store.

func (*Memory) ModelUsage

func (m *Memory) ModelUsage(id string) ([]Columns, error)

ModelUsage implements Store.

func (*Memory) Name

func (m *Memory) Name() string

Name implements Store.

func (*Memory) Orphans

func (m *Memory) Orphans(known map[string]bool) ([]*Stray, error)

Orphans implements Store.

func (*Memory) Sessions

func (m *Memory) Sessions() ([]*Session, []*Stray, error)

Sessions implements Store.

func (*Memory) SystemPrompt

func (m *Memory) SystemPrompt(hash string) (string, bool)

SystemPrompt implements Store.

func (*Memory) Version

func (m *Memory) Version() (int, bool)

Version implements Store.

type Message

type Message struct {
	// Unit names the row in findings: <database>:messages/<id>.
	Unit string

	ID        int64
	SessionID string
	Role      string
	Content   Content
	// ToolCalls is an assistant's calls, those that decoded; BadCalls
	// the entries of the column that are not objects.
	ToolCalls  []*ToolCall
	BadCalls   []json.RawMessage
	ToolCallID string
	ToolName   string
	Time       time.Time
	// FinishReason is the provider's, as Hermes recorded it.
	FinishReason string
	// Reasoning and ReasoningContent are the reasoning texts; a nil
	// ReasoningContent is a null column, which Hermes tells from an
	// empty one.
	Reasoning        string
	ReasoningContent *string
	// ReasoningDetails, CodexReasoningItems and CodexMessageItems are
	// the reasoning carriers, as JSON arrays when they hold one.
	ReasoningDetails    json.RawMessage
	CodexReasoningItems json.RawMessage
	CodexMessageItems   json.RawMessage
	// APIContent is the text Hermes sent in place of the content, and
	// HasAPIContent says the row holds a non-empty one.
	APIContent    string
	HasAPIContent bool
	DisplayKind   string
	// DisplayMetadata is the display_metadata column when it holds a
	// JSON object, once unwrapped from the string layer rows written
	// before Hermes guarded it hold.
	DisplayMetadata json.RawMessage
	// Active and Compacted are the row's flags; a database without
	// the columns has every row active. Summary is
	// _compressed_summary, and HasSummary says the database has the
	// column.
	Active, Compacted   bool
	Summary, HasSummary bool
	Observed            bool

	// Cols is the row as read.
	Cols Columns
	// Problems lists the columns that do not hold what Hermes writes,
	// each kept as written and read as Hermes reads it.
	Problems []string
	// Err says why the row cannot be read.
	Err error
}

Message is one row of the messages table: the columns a converter reads, and the row itself.

func DecodeMessageRow

func DecodeMessageRow(unit string, cols Columns) *Message

DecodeMessageRow reads a row of the messages table.

type Relation

type Relation int

Relation is how a session relates to the session its parent_session_id names, as Hermes tells it.

const (
	// Root is a session with no parent.
	Root Relation = iota
	// Continuation is a compression child: its parent ended with
	// end_reason "compression" and it carries the conversation on.
	Continuation
	// Branch is a /branch copy: model_config._branched_from names the
	// parent, or, before the marker, the parent ended "branched" no
	// later than the child started.
	Branch
	// Reset is a reset child: a new conversation on the parent's chat,
	// marked by _reset_from, or, before the marker, a parent ended at a
	// reset boundary on the child's routing key.
	Reset
	// Delegate is a sub-agent's session: _delegate_from names the
	// parent, its source is "subagent", or nothing else tells it.
	Delegate
	// Tool is a session a tool ran, source "tool".
	Tool
)

func Classify

func Classify(s, parent *Session) (rel Relation, marked bool)

Classify tells a session's relation to its parent, the row its parent_session_id names, which may be nil when the database does not hold it. It follows Hermes' predicates: _is_explicit_fork_child_row (a tool session, or a marker that names the parent), then _is_compression_child_row (a parent ended "compression"), then the legacy branch and reset heuristics of _BRANCH_CHILD_SQL and _legacy_reset_child_sql, and else a sub-agent run, as _ephemeral_child_sql takes every other child. One rule is this package's: a session whose source is "subagent" is a sub-agent whatever its parent's end, unless its parent is one too, whose compression child it may be. marked says the relation was told by a marker or a source rather than by a heuristic.

func (Relation) String

func (r Relation) String() string

String names the relation as the documents do.

type Session

type Session struct {
	// Unit names the row in findings: <database>:sessions/<id>.
	Unit string

	ID       string
	Source   string
	ParentID string
	Model    string
	// ModelConfig is the model_config column when it holds a JSON
	// object.
	ModelConfig json.RawMessage
	// SystemPrompt is the prompt column of the row, and
	// SystemPromptHash the key of system_prompts it names, from schema
	// version 25.
	SystemPrompt     string
	SystemPromptHash string
	Started          time.Time
	Ended            time.Time
	EndReason        string
	SessionKey       string
	CWD              string
	GitBranch        string
	GitRepoRoot      string
	Title            string

	// Cols is the row as read.
	Cols Columns
	// Problems lists the columns that do not hold what Hermes writes,
	// each kept as written.
	Problems []string
	// Err says why the row cannot be read; only Unit and, when the row
	// gave one, ID are then set.
	Err error
}

Session is one row of the sessions table: the columns a converter reads, and the row itself.

func DecodeSessionRow

func DecodeSessionRow(unit string, cols Columns) *Session

DecodeSessionRow reads a row of the sessions table.

func (*Session) Marker

func (s *Session) Marker(name string) string

Marker returns a model_config marker naming another session, such as _branched_from; empty when the config holds none.

type Store

type Store interface {
	// Name names the store in findings: the database's file name.
	Name() string
	// Version is the schema version the database records; false when
	// it records none.
	Version() (int, bool)
	// Sessions lists every row of sessions, and the rows that cannot
	// be read as one.
	Sessions() ([]*Session, []*Stray, error)
	// Messages reads a session's rows of messages in id order, Hermes'
	// order, never the timestamps'.
	Messages(sessionID string) ([]*Message, error)
	// SystemPrompt reads system_prompts' prompt of a hash.
	SystemPrompt(hash string) (string, bool)
	// ModelUsage reads a session's rows of session_model_usage.
	ModelUsage(sessionID string) ([]Columns, error)
	// Delegations reads async_delegations.
	Delegations() ([]*Delegation, error)
	// Orphans lists the rows of messages whose session is none of the
	// set.
	Orphans(sessions map[string]bool) ([]*Stray, error)
}

Store is the rows of one Hermes database. Rows are read on demand, a session at a time.

type Stray

type Stray struct {
	Unit string
	// Session names the session it was read with, when it was.
	Session string
	// Kind says what the row is: "session" or "message".
	Kind string
	Why  string
	// Malformed says the row could not be read at all.
	Malformed bool
}

Stray is a row read that belongs to no session it can be converted with, or that cannot be read.

type ToolCall

type ToolCall struct {
	ID       string    `json:"id"`
	CallID   string    `json:"call_id"`
	Type     string    `json:"type"`
	Function *Function `json:"function"`

	Members
}

ToolCall is one entry of an assistant row's tool_calls: OpenAI's {id, type, function: {name, arguments}}, and on a Responses route the call_id the pairing uses. Undeclared members are kept.

func DecodeToolCalls

func DecodeToolCalls(data []byte) (calls []*ToolCall, bad []json.RawMessage, err error)

DecodeToolCalls reads a tool_calls column: a JSON array of calls. An entry that is not an object is returned in bad. err is set when the column is not a JSON array, which Hermes reads as no calls; a single object is read as a list of one, as _as_list does.

func (*ToolCall) Aliases

func (c *ToolCall) Aliases() []string

Aliases are every spelling that names the call, as Hermes pairs a result with it (agent/message_sanitization.py tool_call_id_variants): its call_id, id and response_item_id, trimmed, and each part of one of the form "a|b".

func (*ToolCall) ArgumentsText

func (c *ToolCall) ArgumentsText() string

ArgumentsText is the call's arguments as the string a function call carries: a JSON string's text, any other JSON value compacted, "{}" for none.

func (ToolCall) MarshalJSON

func (c ToolCall) MarshalJSON() ([]byte, error)

MarshalJSON writes the call back as it was read.

func (*ToolCall) Name

func (c *ToolCall) Name() string

Name is the function's name.

func (*ToolCall) PairingID

func (c *ToolCall) PairingID() string

PairingID is the call's ID as Hermes pairs its result: coalesce_tool_call_id, call_id before id, and of a bridge ID "<call_id>|<item id>" the part before the bar.

func (*ToolCall) UnmarshalJSON

func (c *ToolCall) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the call, keeping undeclared members and members of another JSON type.

Directories

Path Synopsis
Package convert converts Hermes Agent's sessions into the Agent Session Format: every session of a state.db, each with the continuations its compressions started and the sub-agents it ran.
Package convert converts Hermes Agent's sessions into the Agent Session Format: every session of a state.db, each with the continuations its compressions started and the sub-agents it ran.
Package sqlite opens Hermes Agent's database, state.db, and reads its rows as the hermes package's types: sessions, messages, system_prompts, session_model_usage and async_delegations.
Package sqlite opens Hermes Agent's database, state.db, and reads its rows as the hermes package's types: sessions, messages, system_prompts, session_model_usage and async_delegations.

Jump to

Keyboard shortcuts

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