> ## Documentation Index
> Fetch the complete documentation index at: https://lab.pollack.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Agent Journal 1.11.0 core, storage, event, evaluation, and capture APIs

This page describes the public API shipped in Agent Journal 1.11.0.

## Added or changed in 1.11.0

| Module | Added | Changed |
| - | - | - |
| `journal-core` | `JsonFileStorage` header keys `producer` and `producerVersion`; `SourceRecordings` (`find`, `newRecording`, `requireComplete`, `Outcome`); `TokenUsage.cacheHitRatio()` with a new formula (cache reads over input plus cache reads, between 0 and 1) | `TokenUsage.total()` is input plus output and no longer adds thinking tokens on top of output; `effectiveInputTokens()` is deprecated and returns the input tokens; the schema-version rule is corrected (a stored enum gaining a value is a schema change) and a newer schema version is refused with an error naming both versions; `RunStatus.CRASHED` is documented as "ended without a successful end observed" |
| `claude-code-capture` | `SubagentCapture`; `PhaseCapture.subagents()`, `subagentTracksAvailable()`, `reportedSubagentStats()`, `subagentsWithoutTrack()`, `sessionCost()`, `costBasis()`; `SessionCost`; `SessionLogParser.withSessionBaseline(current, previous)`; `RunRecorder.recordOnce(experimentId, capture, configure)`, `sourceKeyOf(capture)`, `SOURCE_KIND`; `BaseRunRecorder.META_*`, `CONFIG_SPAWN_TOOL_USE_ID`, `TAG_TRACK` | `RunRecorder.close()` without `finish()` ends the run `CRASHED`; `finish()` ends the run before throwing a lost-derived-events error; `PhaseCapture.totalTokens()` and `aggregateUsage()` take output tokens from the result message and count input and cache tokens once per message; one turn per message |
| `codex-cli-capture` | `CodexRollout`, `CodexRollouts`, `CodexSubagentCapture`; `CodexSessionParser.parse(CodexRollouts, phaseName, promptText)`; `CodexPhaseCapture.subagents()`, `subagentTracksAvailable()`, `spawnedThreadIds()`, `subagentsWithoutTrack()`; `CodexRunRecorder.recordOnce(experimentId, capture, configure)`, `SOURCE_KIND`, `META_*`; `CodexJournalSteps.forSubagent` | `CodexPhaseCapture.tokenUsage()` input excludes cached input |
| `grok-cli-capture`, `junie-cli-capture` | none | tool `durationMs` is -1 when unmeasured |
| build | Jackson 2.22.3 and Jackson 3 3.2.3 (CVE-2026-89407, CVE-2026-89425, CVE-2026-91776, CVE-2026-91777); published module POMs inline the licence and project metadata | none |

### Migrating a caller from 1.10.1

Three kinds of compatibility apply, and they differ:

* **Constructors.** Every earlier `PhaseCapture` and `CodexPhaseCapture` constructor is kept, so code that builds a capture with positional arguments compiles unchanged. A kept constructor fills the new components with empty values (no sub-agents, tracks unavailable, no session cost).
* **Record patterns.** A record pattern names every component. `PhaseCapture` grew from 22 to 26 components (`subagents`, `subagentTracksAvailable`, `reportedSubagentStats`, `sessionCost` appended), and `CodexPhaseCapture` from 14 to 17 (`subagents`, `subagentTracksAvailable`, `spawnedThreadIds`). A pattern written against 1.10.1 does not compile against 1.11.0 until the new components are added.
* **JSON readers.** A `PhaseCapture` serialised with Jackson has four more properties and a `CodexPhaseCapture` three; readers that ignore unknown properties keep working. Journal files written by 1.11.0 load in 1.10.1 readers (no new record field, no new enum value), and 1.11.0 readers load older files.

The released agent-client (0.31.0) pins Journal 1.10.0 and calls none of the 1.11.0 additions; see the [sub-agent capture guide](/docs/agent-journal/subagent-capture#requirements).

## Coordinates

| Artifact | Purpose | Java |
| - | - | - |
| `io.github.markpollack:journal-core:1.11.0` | Core domain, storage, evaluation, feedback, and traces | 17+ |
| `io.github.markpollack:claude-code-capture:1.11.0` | Claude Code capture | 21+ runtime |
| `io.github.markpollack:gemini-cli-capture:1.11.0` | Gemini CLI capture | 21+ runtime |
| `io.github.markpollack:grok-cli-capture:1.11.0` | Grok CLI capture | 17+ |
| `io.github.markpollack:codex-cli-capture:1.11.0` | Codex CLI capture | 17+ |
| `io.github.markpollack:antigravity-cli-capture:1.11.0` | Antigravity CLI capture | 17+ |
| `io.github.markpollack:junie-cli-capture:1.11.0` | Junie CLI capture | 17+ |

## `Journal`

| Method | Result | Notes |
| - | - | - |
| `run(String experimentId)` | `RunBuilder` | Primary run factory |
| `experiment(String experimentId)` | `Experiment` | Gets or creates an experiment |
| `experiment(String id, Experiment.Builder builder)` | `Experiment` | Builder is ignored if the experiment already exists |
| `configure(JournalStorage storage)` | `void` | Call before creating runs |
| `storage()` | `JournalStorage` | Returns the global backend |
| `registerEventType(String, Class<? extends JournalEvent>)` | `void` | Registers an external subtype on the configured storage |
| `reset()` | `void` | Resets storage configuration and the experiment cache |

## `RunBuilder`

| Method | Purpose |
| - | - |
| `name(String)` | Human-readable run name |
| `agent(String)` | Agent identifier |
| `task(String)` | Accepts a task identifier; 1.11.0 does not persist it or act on it |
| `repository(String)` | Accepts a repository path; 1.11.0 does not persist it or act on it |
| `config(Config)` | Replace the run configuration |
| `config(String, Object)` | Add one immutable input value |
| `tags(Tags)` | Replace tags |
| `tag(String, String)` | Add one tag |
| `previousRun(String)` | Link a retry to the prior run ID |
| `parentRun(String)` | Link a child run to a parent run ID |
| `start()` | Create a `RUNNING` `Run` |

There is no `config(Map)` overload in 1.11.0.
The `task(String)` and `repository(String)` setters are present, but the 1.11.0 run implementation does not consume either stored builder value.

## `Run`

| Area | Methods |
| - | - |
| Identity | `id()`, `name()`, `experiment()`, `agentId()`, `previousRunId()`, `parentRunId()` |
| Data | `config()`, `summary()`, `tags()`, `status()` |
| Execution | `logEvent(JournalEvent)`, `logMetric(...)` |
| Analysis | `logDerivedEvent(DerivedEvent)` |
| Artifacts | `logArtifact(String, String)`, `logArtifact(String, byte[], Map<String,Object>)` |
| Instruments | `metrics()`, `calls()` |
| Lifecycle | `setSummary(...)`, `fail(Throwable)`, `finish(RunStatus)`, `close()` |

`RunStatus` is `RUNNING`, `FINISHED`, `FAILED` or `CRASHED`.
A run that finishes as failed or crashed records `success=false`.
A plain `Run` closed without `finish(...)` ends `FINISHED`; a Claude Code `RunRecorder` closed without `finish()` ends `CRASHED` since 1.11.0.

## Event factories

`JournalEvent` is extensible.
Built-in event records include timestamps and expose their Java `type()` value; file serialization uses the registered `@type` name.

| Event | Common factory or builder |
| - | - |
| `LLMCallEvent` | `of(model, inputTokens, outputTokens, costUsd)`, `builder()` |
| `ToolCallEvent` | `success(tool, input, output, durationMs)`, `failure(tool, input, error, durationMs)`, `builder()` |
| `StateChangeEvent` | `of(from, to, reason)` |
| `MetricEvent` | `of(name, value, tags)` |
| `CustomEvent` | `of(name)`, `of(name, attributes)` |
| `GitPatchEvent` | `of(baseBranch, fileChanges)` |
| `GitCommitEvent` | `of(sha, message, branch)` |
| `GitBranchEvent` | `created(branchName, fromRef)`, `checkedOut(branchName)`, `deleted(branchName)` |
| `GitPullRequestEvent` | `created(...)`, `updated(...)`, `merged(...)`, `closed(...)` |

### Token, cost, and timing values

```java theme={null}
TokenUsage simple = TokenUsage.of(1200, 450);
TokenUsage withThinking = TokenUsage.of(1200, 450, 300);

TokenUsage allFields = new TokenUsage(
        1200, 450, 300, 200, 800, 0);

CostBreakdown cost = CostBreakdown.of(0.018, 0.00675);
TimingInfo timing = TimingInfo.of(2500, 2100, 400);
```

The `TokenUsage` constructor order is input, output, thinking, cache creation, cache read, and tool-use tokens.
There is no five-argument `TokenUsage.of(...)` overload in 1.11.0.
Thinking tokens are a subset of output tokens: `total()` is input plus output, `cacheHitRatio()` is cache reads divided by input plus cache reads, and `effectiveInputTokens()` is deprecated.
Input tokens exclude cache reads for every provider.

## `JournalStorage`

| Area | Operations |
| - | - |
| Experiments | `saveExperiment`, `loadExperiment`, `listExperiments`, `experimentExists` |
| Runs | `saveRun`, `loadRun`, `listRuns`, `runExists` |
| Execution events | `appendEvent`, `loadEvents` |
| Derived events | `appendDerivedEvent`, `loadDerivedEvents`, `persistsDerivedEvents` |
| Feedback | `appendFeedback`, `loadFeedback` |
| Artifacts | `saveArtifact`, `loadArtifact`, `listArtifacts` |
| External event types | `registerEventSubtype` |

`JsonFileStorage` implements every operation and reports durable derived-event persistence.
`InMemoryStorage` implements every operation in memory and reports the default non-durable derived-event behavior.

The header line of `events.jsonl` and `analysis.jsonl` carries `schemaVersion` (still 1), and since 1.11.0 `producer` and `producerVersion`; a header without `producerVersion` was written by 1.10.1 or earlier.
A file with a newer schema version than the reader knows, or a non-numeric one, is refused.
Event types registered with `Journal.registerEventType` are kept for the whole process and apply to storage configured later.

### `SourceRecordings`

`SourceRecordings.find(experimentId, sourceKind, sourceKey)` looks a recorded execution up by the key its run carries in `capture.sourceKind` and `capture.sourceKey`; `requireComplete` fails on a recording that has not ended or ended with an error.
`SourceRecordings.Outcome(runId, recordedNow)` is what `recordOnce` returns.

## Evaluation API

`EvalSubjectSources.fromJournal(storage, experimentId, runId)` reads a run and produces subjects.
`EvalSubjectSources.fromEvents(events, runId)` adapts an existing event list.

`EvalSubjectQuery` supports:

| Method | Purpose |
| - | - |
| `from(EvalSubjectSource)` | Start a query |
| `kind(EvalSubjectKind)` | Filter by kind |
| `where(Predicate<EvalSubject>)` | Add a predicate |
| `toSet()` | Materialize an `EvalSubjectSet` |
| `groupBy(Function)` | Group into subject sets |
| `countBy(Function)` | Count by classifier |
| `count()` | Count matches |

## Capture parsers

### Claude Code

```java theme={null}
SessionLogParser.parse(response, phaseName, promptText)
SessionLogParser.parse(response, phaseName, promptText, traceFile)
SessionLogParser.parse(response, phaseName, promptText, traceFile, contentMode)
SessionLogParser.parse(response, phaseName, promptText, traceFile, contentMode, rawMode)
SessionLogParser.parse(response, phaseName, promptText, traceFile, contentMode, rawMode, maxTurns)
```

`response` is the live `Iterator<ParsedMessage>` returned by the SDK; the parser does not read a session log file.
The `maxTurns` overload records the turn ceiling the caller launched the run with, because Claude Code does not report it back; pass `SessionLogParser.UNKNOWN_MAX_TURNS` (`-1`) when no ceiling was set. The shorter overloads pass `-1`.
The default content mode for trace-writing overloads is `TRUNCATED`; the default raw mode is `NONE`.

```java theme={null}
SessionLogParser.withSessionBaseline(current, previous)
RunRecorder.recordOnce(experimentId, phase, configure)
RunRecorder.sourceKeyOf(phase)
```

`withSessionBaseline` returns a copy of a later capture of the same session whose cost, per-model costs and API time are the difference from `previous`, with the running totals in `sessionCost()` and `costBasis()` set to `session_delta`; it never subtracts across session ids.
`recordOnce` records an execution at most once per experiment and storage root and returns a `SourceRecordings.Outcome`; `configure` is a `UnaryOperator<RunBuilder>` or `null`.

`RunRecorder` wraps an open `Run`, inherits `recordPhase(PhaseCapture)`, exposes `run()` and `lenient()`, and owns `finish()`/`close()`.
Only `finish()` records a completed run; `close()` without it records `CRASHED`.
A capture with sub-agents is written as one run per sub-agent, linked by `parentRunId`, tagged `track=subagent`; see [Sub-agent Capture](/docs/agent-journal/subagent-capture).

### Gemini CLI

```java theme={null}
GeminiSessionParser.parse(result, phaseName, promptText)
GeminiSessionParser.parse(result, phaseName, promptText, traceFile)
GeminiSessionParser.parse(result, phaseName, promptText, traceFile, contentMode)
```

`result` is a Gemini SDK `QueryResult`.
`GeminiRunRecorder.recordPhase(GeminiPhaseCapture)` records the core and derived projections.

### Codex CLI

```java theme={null}
CodexSessionParser.parse(rolloutFile, phaseName, promptText)
CodexSessionParser.parse(reader, phaseName, promptText)
CodexSessionParser.parse(rollouts, phaseName, promptText)
CodexRunRecorder.recordOnce(experimentId, phase, configure)
```

`rollouts` is a `CodexRollouts` holding one `CodexRollout(threadId, parentThreadId, sourcePath, lines)` per thread, the root first; `CodexRollouts.root()` and `children()` expose them.
The single-file overloads are unchanged.
`CodexRunRecorder.recordPhase(CodexPhaseCapture)` records the core and derived projections; `recordOnce` is keyed by the root thread id.

### Junie CLI

```java theme={null}
JunieSessionParser.parse(eventsFile, phaseName, promptText)
JunieSessionParser.parse(reader, phaseName, promptText)
```

`eventsFile` is a `Path` to Junie's session `events.jsonl` and `reader` is a `BufferedReader` over the same content; both throw `IOException`.
`JunieRunRecorder.recordPhase(JuniePhaseCapture)` records the core and derived projections.

## Storage cautions

* `JsonFileStorage` does not validate path containment for caller-supplied identifiers or artifact names in 1.11.0.
* Use one writer per run and do not assume multi-process safety.
* File-backed load methods read the requested JSONL file into memory.
* Portable traces can contain sensitive content; see [Capture SDK Sessions](/docs/agent-journal/capture-sessions).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.