Documentation
¶
Overview ¶
Package claudecode reads Claude Code session transcripts: the JSONL files under ~/.claude/projects, one per session and one per sub-agent. It imports the standard library alone.
The format is not specified. Anthropic calls the line union CLI-internal and changes it between releases; this package was written from the transcripts of Claude Code 2.1.271 to 2.1.300 and the Agent SDK's typings, which type the messages, the compaction metadata and the tool output sidecars but not the lines. So the reader keeps what it does not type: every object keeps its undeclared members and writes them back, a line of a shape it does not expect keeps its raw bytes, and a line that is not JSON is recorded and skipped, wherever it is in the file.
Read and Open read; BuildTree applies the rules a reader of the format needs to see the conversation, which docs/formats/claudecode.md sets out. Converting to the Agent Session Format is the convert subpackage's.
Index ¶
- Constants
- func FindSessions(root string) ([]string, error)
- type Agent
- type AgentMeta
- type Attachment
- type Call
- type CompactMetadata
- type Content
- type ContentBlock
- type Line
- func (l *Line) InTree() bool
- func (l *Line) IsSynthetic() bool
- func (l *Line) IsToolResult() bool
- func (l Line) MarshalJSON() ([]byte, error)
- func (l *Line) PromptSnapshot() (*PromptSnapshot, bool)
- func (l *Line) Text() string
- func (l *Line) Time() time.Time
- func (l *Line) ToolResults() []*ContentBlock
- func (l *Line) ToolUses() []*ContentBlock
- func (l *Line) UnmarshalJSON(data []byte) error
- type LineError
- type MediaSource
- type Members
- type Message
- type OutputTokensDetails
- type PreservedMessages
- type PreservedSegment
- type PromptSnapshot
- type Rendered
- type RequestShape
- type Response
- type Session
- type SnapshotTool
- type ToolDef
- type Transcript
- type Tree
- type Unit
- type Unplaced
- type Usage
Constants ¶
const ( TypeUser = "user" TypeAssistant = "assistant" TypeAttachment = "attachment" TypeSystem = "system" )
Line types that form the conversation tree: they carry uuid and parentUuid.
const ( TypeAITitle = "ai-title" TypeCustomTitle = "custom-title" TypeSummary = "summary" TypeTag = "tag" TypeLastPrompt = "last-prompt" TypeMode = "mode" TypePermissionMode = "permission-mode" TypeAtisLatch = "atis-latch" TypeQueueOperation = "queue-operation" TypePRLink = "pr-link" TypeFileHistorySnapshot = "file-history-snapshot" TypeFileHistoryDelta = "file-history-delta" TypeCostState = "cost-state" TypeBridgeSession = "bridge-session" TypeForkContextRef = "fork-context-ref" TypeAPIRequest = "api-request" TypeAPIRequestBlob = "api-request-blob" TypeAPIRequestShape = "api-request-shape" )
Side record types: state the CLI appends beside the tree, with no uuid, the last one written in force.
const ( SubtypeCompactBoundary = "compact_boundary" SubtypeTurnDuration = "turn_duration" SubtypeLocalCommand = "local_command" )
System line subtypes the converter treats specially. Others are kept as they are.
const ( BlockText = "text" BlockThinking = "thinking" BlockRedactedThinking = "redacted_thinking" BlockToolUse = "tool_use" BlockToolResult = "tool_result" BlockImage = "image" BlockDocument = "document" BlockToolReference = "tool_reference" )
Content block types.
const MaxLineSize = 256 << 20
MaxLineSize bounds one line. A longer line is reported as malformed and skipped rather than read into memory.
const SyntheticModel = "<synthetic>"
SyntheticModel is the model a line the CLI wrote itself, such as an API error, names.
Variables ¶
This section is empty.
Functions ¶
func FindSessions ¶
FindSessions returns the main transcripts under root: every <session-id>.jsonl, at any depth, its name a UUID as session IDs are. root may be ~/.claude/projects, one project directory, or a single file. A session's own directory beside its transcript, <session-id>/, holds its sub-agents and stored tool results and no main transcript, so it is not searched, even as root. No directory is skipped for its name alone: a project or fixture directory called "subagents" or "memory" is searched like any other.
Types ¶
type Agent ¶
type Agent struct {
// ID is the agentId, from the file name agent-<agentId>.jsonl.
ID string
Meta *AgentMeta
Transcript *Transcript
// MetaRaw and MetaErr hold a meta file that did not decode; Meta is
// nil then.
MetaRaw json.RawMessage
MetaErr error
}
Agent is one sub-agent: its transcript and the meta file beside it. Either may be missing; an agent that never wrote a line has a meta file alone.
type AgentMeta ¶
type AgentMeta struct {
AgentType string `json:"agentType"`
Description string `json:"description"`
ToolUseID string `json:"toolUseId"`
ParentAgentID string `json:"parentAgentId"`
IsFork bool `json:"isFork"`
Name string `json:"name"`
TeamName string `json:"teamName"`
Model string `json:"model"`
SpawnDepth int `json:"spawnDepth"`
// RequestShape is "foreground" for an agent the call waited on and
// "background" for one it started and left running.
RequestShape string `json:"requestShape"`
Members
// Raw is the file as read.
Raw json.RawMessage `json:"-"`
}
AgentMeta is a sub-agent's agent-<agentId>.meta.json.
func (AgentMeta) MarshalJSON ¶
MarshalJSON writes the meta file back as it was read.
func (*AgentMeta) UnmarshalJSON ¶
UnmarshalJSON decodes the meta file, keeping undeclared members.
type Attachment ¶
Attachment is context the CLI injected into a turn. Type discriminates some thirty kinds; their members stay in Extra.
func (Attachment) MarshalJSON ¶
func (a Attachment) MarshalJSON() ([]byte, error)
MarshalJSON writes the attachment back as it was read.
func (*Attachment) Member ¶
func (a *Attachment) Member(name string, v any) bool
Member decodes an attachment member into v; false when it is absent or does not decode.
func (*Attachment) UnmarshalJSON ¶
func (a *Attachment) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes an attachment, keeping undeclared members.
type Call ¶
type Call struct {
Use *ContentBlock
// Line holds the tool_use block.
Line *Line
// Result is the user line holding the tool_result; nil when the call
// was never answered.
Result *Line
// ResultBlock is the tool_result block within Result.
ResultBlock *ContentBlock
}
Call is one tool_use block of a response and the line that answered it, if any.
type CompactMetadata ¶
type CompactMetadata struct {
Trigger string `json:"trigger"`
PreTokens int64 `json:"preTokens"`
PostTokens int64 `json:"postTokens"`
PreservedSegment *PreservedSegment `json:"preservedSegment"`
PreservedMessages *PreservedMessages `json:"preservedMessages"`
Members
}
CompactMetadata describes a compaction on its boundary line.
func (*CompactMetadata) FirstPreserved ¶
func (c *CompactMetadata) FirstPreserved() string
FirstPreserved returns the uuid of the earliest line the compaction kept verbatim, or "" when it kept none.
func (CompactMetadata) MarshalJSON ¶
func (c CompactMetadata) MarshalJSON() ([]byte, error)
MarshalJSON writes the metadata back as it was read.
func (*CompactMetadata) UnmarshalJSON ¶
func (c *CompactMetadata) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the metadata, keeping undeclared members.
type Content ¶
type Content struct {
// Text is the content when it was a string.
Text string
// IsText says the content was a string.
IsText bool
Blocks []*ContentBlock
}
Content is a message's content: a string, or a list of blocks.
func (Content) MarshalJSON ¶
MarshalJSON writes the string or the blocks.
func (*Content) UnmarshalJSON ¶
UnmarshalJSON accepts a string or an array of blocks.
Each block decodes on its own, so a block of a shape this package does not type, or a null, is kept raw rather than failing the message: see ContentBlock.Raw.
type ContentBlock ¶
type ContentBlock struct {
Type string `json:"type"`
// text
Text string `json:"text"`
// thinking
Thinking string `json:"thinking"`
Signature string `json:"signature"`
// redacted_thinking
Data string `json:"data"`
// tool_use, server_tool_use
ID string `json:"id"`
Name string `json:"name"`
Input json.RawMessage `json:"input"`
// tool_result
ToolUseID string `json:"tool_use_id"`
Content *Content `json:"content"`
IsError bool `json:"is_error"`
// image, document
Source *MediaSource `json:"source"`
// tool_reference
ToolName string `json:"tool_name"`
Members
// Raw is set, with Err, when the block is not of the shapes this
// type declares: a server tool result whose content is an object, a
// block whose source is a string, a null. Only Type is decoded
// then, when the block has a string type; Raw is written back
// verbatim.
Raw json.RawMessage `json:"-"`
Err error `json:"-"`
}
ContentBlock is one Anthropic content block. Type says which members apply.
func (*ContentBlock) IsRaw ¶
func (b *ContentBlock) IsRaw() bool
IsRaw reports whether the block was kept raw: see Raw.
func (ContentBlock) MarshalJSON ¶
func (b ContentBlock) MarshalJSON() ([]byte, error)
MarshalJSON writes the block back as it was read.
func (*ContentBlock) UnmarshalJSON ¶
func (b *ContentBlock) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes a block, keeping undeclared members. It never fails: a block it cannot type is kept in Raw.
type Line ¶
type Line struct {
Type string `json:"type"`
// The envelope of the tree's lines.
UUID string `json:"uuid"`
ParentUUID string `json:"parentUuid"`
LogicalParentUUID string `json:"logicalParentUuid"`
IsSidechain bool `json:"isSidechain"`
SessionID string `json:"sessionId"`
AgentID string `json:"agentId"`
Timestamp string `json:"timestamp"`
CWD string `json:"cwd"`
GitBranch string `json:"gitBranch"`
Version string `json:"version"`
UserType string `json:"userType"`
Entrypoint string `json:"entrypoint"`
// user and assistant.
Message *Message `json:"message"`
// user.
IsMeta bool `json:"isMeta"`
IsCompactSummary bool `json:"isCompactSummary"`
IsVisibleInTranscriptOnly bool `json:"isVisibleInTranscriptOnly"`
ToolUseResult json.RawMessage `json:"toolUseResult"`
SourceToolAssistantUUID string `json:"sourceToolAssistantUUID"`
PermissionMode string `json:"permissionMode"`
// assistant.
RequestID string `json:"requestId"`
APIBlockIndex int `json:"apiBlockIndex"`
RequestedModel string `json:"requestedModel"`
WireToolInputs map[string]json.RawMessage `json:"wireToolInputs"`
IsAPIErrorMessage bool `json:"isApiErrorMessage"`
Error json.RawMessage `json:"error"`
APIErrorStatus int `json:"apiErrorStatus"`
// attachment.
Attachment *Attachment `json:"attachment"`
Rendered []Rendered `json:"rendered"`
RenderedRole string `json:"renderedRole"`
RenderedInHumanTurn []Rendered `json:"renderedInHumanTurn"`
// system.
Subtype string `json:"subtype"`
Content json.RawMessage `json:"content"`
CompactMetadata *CompactMetadata `json:"compactMetadata"`
// Side records.
AITitle string `json:"aiTitle"`
CustomTitle string `json:"customTitle"`
Summary string `json:"summary"`
LeafUUID string `json:"leafUuid"`
LastPrompt string `json:"lastPrompt"`
ParentSessionID string `json:"parentSessionId"`
ParentLastUUID string `json:"parentLastUuid"`
Shape *RequestShape `json:"shape"`
Members
// No is the line's 1-based number in its file.
No int `json:"-"`
// Raw is the line as read, without NUL bytes.
Raw json.RawMessage `json:"-"`
// Err is set when the line is JSON but does not decode as a line of
// this package's types. The envelope members that are strings
// (type, uuid, parentUuid, logicalParentUuid, timestamp, cwd,
// gitBranch, version, sessionId, agentId, subtype) are still read,
// so the line keeps its place in the tree; Raw is written back.
Err error `json:"-"`
}
Line is one line of a transcript. It holds the members of every line type; Type says which apply. Members the type does not declare are kept in Extra and written back.
func (*Line) InTree ¶
InTree reports whether the line is a node of the conversation tree, rather than a side record.
func (*Line) IsSynthetic ¶
IsSynthetic reports whether the line is an assistant line the CLI wrote itself rather than the model: an API error or an interruption.
func (*Line) IsToolResult ¶
IsToolResult reports whether the line carries a tool result.
func (Line) MarshalJSON ¶
MarshalJSON writes the line back as it was read.
func (*Line) PromptSnapshot ¶
func (l *Line) PromptSnapshot() (*PromptSnapshot, bool)
PromptSnapshot decodes a prompt_snapshot attachment; false for any other line.
func (*Line) ToolResults ¶
func (l *Line) ToolResults() []*ContentBlock
ToolResults returns the line's tool_result blocks.
func (*Line) ToolUses ¶
func (l *Line) ToolUses() []*ContentBlock
ToolUses returns the line's tool_use blocks.
func (*Line) UnmarshalJSON ¶
UnmarshalJSON decodes a line, keeping undeclared members.
type MediaSource ¶
type MediaSource struct {
Type string `json:"type"`
MediaType string `json:"media_type"`
Data string `json:"data"`
URL string `json:"url"`
FileID string `json:"file_id"`
Members
}
MediaSource is where an image's or a document's bytes are.
func (MediaSource) MarshalJSON ¶
func (s MediaSource) MarshalJSON() ([]byte, error)
MarshalJSON writes the source back as it was read.
func (*MediaSource) UnmarshalJSON ¶
func (s *MediaSource) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes a source, keeping undeclared members.
type Members ¶
Members is embedded in every object this package decodes. It keeps the members the Go type does not declare, and remembers which declared members the input carried and which were null, so an object decoded and encoded again is the same JSON value.
type Message ¶
type Message struct {
ID string `json:"id"`
Type string `json:"type"`
Role string `json:"role"`
Model string `json:"model"`
Content Content `json:"content"`
StopReason string `json:"stop_reason"`
StopSequence string `json:"stop_sequence"`
Usage json.RawMessage `json:"usage"`
Members
}
Message is an Anthropic Messages API message: a user turn, or one content block of an assistant response.
func (*Message) DecodeUsage ¶
DecodeUsage decodes the message's usage; nil when it has none.
func (Message) MarshalJSON ¶
MarshalJSON writes the message back as it was read.
func (*Message) UnmarshalJSON ¶
UnmarshalJSON decodes a message, keeping undeclared members.
type OutputTokensDetails ¶
OutputTokensDetails breaks down output tokens.
func (OutputTokensDetails) MarshalJSON ¶
func (d OutputTokensDetails) MarshalJSON() ([]byte, error)
MarshalJSON writes the details back as they were read.
func (*OutputTokensDetails) UnmarshalJSON ¶
func (d *OutputTokensDetails) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the details, keeping undeclared members.
type PreservedMessages ¶
type PreservedMessages struct {
AnchorUUID string `json:"anchorUuid"`
UUIDs []string `json:"uuids"`
AllUUIDs []string `json:"allUuids"`
Members
}
PreservedMessages lists the lines a compaction kept: uuids[0] relinks to anchor, each later one to the one before it.
func (PreservedMessages) MarshalJSON ¶
func (p PreservedMessages) MarshalJSON() ([]byte, error)
MarshalJSON writes the list back as it was read.
func (*PreservedMessages) UnmarshalJSON ¶
func (p *PreservedMessages) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the list, keeping undeclared members.
type PreservedSegment ¶
type PreservedSegment struct {
AnchorUUID string `json:"anchorUuid"`
HeadUUID string `json:"headUuid"`
TailUUID string `json:"tailUuid"`
Members
}
PreservedSegment names the run of lines a compaction kept: from head to tail on the path before the boundary, relinked after anchor.
func (PreservedSegment) MarshalJSON ¶
func (s PreservedSegment) MarshalJSON() ([]byte, error)
MarshalJSON writes the segment back as it was read.
func (*PreservedSegment) UnmarshalJSON ¶
func (s *PreservedSegment) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the segment, keeping undeclared members.
type PromptSnapshot ¶
type PromptSnapshot struct {
CLIPrefix string `json:"cliPrefix"`
SystemPrompt []string
Tools []SnapshotTool
// HasTools says the snapshot listed tools; one without them leaves
// the tools as they were.
HasTools bool
// EchoWireToolInputs says the requests replay a tool call's
// arguments as the model wrote them (wireToolInputs) rather than as
// the CLI normalised them.
EchoWireToolInputs bool
}
PromptSnapshot is a prompt_snapshot attachment: the system prompt, and sometimes the tools, of the requests from that point.
type Rendered ¶
Rendered is the text an attachment put into the model's context.
func (Rendered) MarshalJSON ¶
MarshalJSON writes the rendering back as it was read.
func (*Rendered) UnmarshalJSON ¶
UnmarshalJSON decodes the rendering, keeping undeclared members.
type RequestShape ¶
RequestShape is the system prompt and tools of a request, on an api-request-shape line (2.1.300).
func (RequestShape) MarshalJSON ¶
func (s RequestShape) MarshalJSON() ([]byte, error)
MarshalJSON writes the shape back as it was read.
func (*RequestShape) UnmarshalJSON ¶
func (s *RequestShape) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the shape, keeping undeclared members.
type Response ¶
type Response struct {
MessageID string
RequestID string
Lines []*Line
Calls []*Call
// Extra holds result lines that belong to this response and carry
// more than its results: other blocks, or results the tree could
// not place (see [Tree.Unplaced]).
Extra []*Line
// Between holds the lines on the chain from one block of the
// response to the next that are not the response's own, in the
// order the chain runs. Claude Code sends them in the user turn
// after the tool results.
Between []*Line
}
Response is one API response: its assistant lines in apiBlockIndex order and the tool calls they made.
type Session ¶
type Session struct {
// ID is the session's ID: the main transcript's file name.
ID string
Main *Transcript
Agents []*Agent
}
Session is a main transcript and the sub-agent transcripts beside it.
type SnapshotTool ¶
type SnapshotTool struct {
Name string `json:"name"`
Description string `json:"description"`
Schema *ToolDef `json:"schema"`
}
SnapshotTool is one tool of a prompt snapshot.
type ToolDef ¶
type ToolDef struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema json.RawMessage `json:"input_schema"`
Members
}
ToolDef is an Anthropic tool definition.
func (ToolDef) MarshalJSON ¶
MarshalJSON writes the definition back as it was read.
func (*ToolDef) UnmarshalJSON ¶
UnmarshalJSON decodes the definition, keeping undeclared members.
type Transcript ¶
type Transcript struct {
// Path is the file the transcript was read from; empty when it was
// read from a reader.
Path string
Lines []*Line
// Errors lists the lines that could not be read, which the reader
// skipped.
Errors []LineError
// NULLines lists the lines that held NUL bytes, which the reader
// removed before decoding. Claude Code leaves runs of them where a
// write was torn.
NULLines []int
}
Transcript is one JSONL file: a session's main transcript or one sub-agent's.
func Read ¶
func Read(r io.Reader) (*Transcript, error)
Read reads a transcript. A line that is not JSON is recorded in Errors and skipped, wherever it is in the file: Claude Code files hold torn lines mid-file, not only at the end. A line that is JSON but not of the shapes this package declares is kept with Err set. Read fails only when r does.
func ReadFile ¶
func ReadFile(path string) (*Transcript, error)
ReadFile reads a transcript from a file.
func (*Transcript) LineCount ¶
func (t *Transcript) LineCount() int
LineCount is the number of lines the file had: those read and those that could not be.
type Tree ¶
type Tree struct {
// Units are every unit, ordered by the file position of the line
// each starts at.
Units []*Unit
Roots []*Unit
// Head holds the side records written before the first tree line.
Head []*Line
// Orphans are tool_result lines none of whose results answer a call
// in the transcript. Each is also a unit of its own.
Orphans []*Line
// Unplaced are result blocks on a line that belongs to a response
// that are not that response's: a second result for a call already
// answered, a result for another response's call, or for none.
Unplaced []Unplaced
// Dangling lists lines whose parent is not in the transcript; each
// is a root here.
Dangling []*Line
// Cycles lists lines whose parent links led back to themselves; the
// first line met of each cycle is a root here.
Cycles []*Line
// contains filtered or unexported fields
}
Tree is a transcript's conversation as Claude Code's loader sees it, built from the uuid and parentUuid links with the reader's rules applied:
- Every line is indexed before any is linked, since a parent can be written after its child.
- The assistant lines of one API response, one content block each and sharing message.id, are one Response, with the tool_result lines that answer its tool_use blocks. On disk a tool result hangs from the line holding its tool_use while the next block of the response hangs from whatever was written last, so the results of parallel calls are dead ends; inside a Response they are not.
- A line written between two blocks of one response, on the chain that links them (an attachment, a meta message), is in the response too, in Response.Between, so it stays on the path.
- A compact_boundary line is a root on disk; here it hangs from its logicalParentUuid, the line the conversation was at when it was compacted, so the conversation reads through it.
- Timestamps are never used to order anything: they go backwards.
A line that did not decode keeps its place by its uuid and parent. Side records, the lines without a uuid, are attached to the unit of the tree line written before them, in file order.
func BuildTree ¶
func BuildTree(tr *Transcript) *Tree
BuildTree builds the conversation tree of a transcript.
type Unit ¶
type Unit struct {
// Line is the line of a single-line unit; nil for a response.
Line *Line
Response *Response
Parent *Unit
Children []*Unit
// Side holds the side records written after this unit's lines and
// before the next tree line, in file order.
Side []*Line
// Logical says Parent is a compaction's logical parent, not the
// line's parentUuid.
Logical bool
// contains filtered or unexported fields
}
Unit is a node of the tree: one line, or one API response with the lines that answer its tool calls.
type Unplaced ¶
type Unplaced struct {
Line *Line
Block *ContentBlock
// Why is "duplicate", "other-response" or "no-call".
Why string
}
Unplaced is a tool_result block the tree could not give a call.
type Usage ¶
type Usage struct {
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
OutputTokensDetails *OutputTokensDetails `json:"output_tokens_details"`
Members
}
Usage is an Anthropic response's token accounting. Anthropic's input_tokens leaves out the cached tokens, read and written.
func (Usage) MarshalJSON ¶
MarshalJSON writes the usage back as it was read.
func (*Usage) UnmarshalJSON ¶
UnmarshalJSON decodes usage, keeping undeclared members.