Agents today have a limited ability to learn from past experience to improve in the future. Furthermore, agent experience is split across many different harnesses: many users swap between harnesses such as Claude Code, Codex, and Letta Code — and organizations typically have individual users on a wide variety of harnesses depending on personal preference.
Learning across harnesses requires first creating a standard data format for the experience each harness produces. Today, learning is primarily done in token-space, where agents themselves process experience to learn system prompts, skills, or even harness-level modifications. An ideal data format for experience is therefore:
- Standardized across harnesses, to avoid distraction from divergence in formats
- Token-efficient, to enable agents to process experience as efficiently as possible
We are introducing the trajectory package, which formats trajectories across harnesses in a consistent, agent-friendly format. The format is designed for agents reading past sessions for memory formation, and minimizes token overhead from metadata. While projects such as Harbor have introduced normalized formats for agent trajectories (ATIF), those formats are intended for full-fidelity replay and benchmarking — they preserve per-step token metrics, structured tool payloads, and untruncated outputs. When the consumer of the trajectory is an agent, the data should instead be presented in a token-efficient manner and include only the minimal details required to understand what happened in the session.
Trajectory schema
A trajectory is a list of records in a standard format: assistant messages, user messages, reasoning, tool calls, and tool results. The first record contains metadata about the harness the trajectory was created in.
{
"role": "meta",
"source": "codex",
"cwd": "/workspace",
"git_branch": "main",
"model": "gpt-5-codex"
}{
"role": "user",
"content": "Check the current directory.",
"timestamp": "2026-07-10T12:00:00.000Z"
}{
"role": "reasoning",
"content": "The user wants the working directory — I'll run pwd.",
"timestamp": "2026-07-10T12:00:01.000Z"
}{
"role": "assistant",
"content": null,
"tool_calls": [
{ "id": "call_1", "name": "exec_command", "args": "{\"cmd\":\"pwd\"}" }
],
"timestamp": "2026-07-10T12:00:02.000Z"
}{
"role": "tool",
"tool_call_id": "call_1",
"content": "/workspace",
"timestamp": "2026-07-10T12:00:03.000Z"
}The full spec is defined in trajectory-v1.schema.json.
The format keeps only the information needed to understand the agent's experience. It drops harness bookkeeping (per-line envelopes, duplicated payloads, UI event streams, encrypted reasoning blobs), and optionally truncates long tool results. On sessions we sampled, this results in a ~5x reduction in token counts compared to native session formats.
| Claude Code | Codex | |
|---|---|---|
| Native | baseline951,115 | baseline3,919,385 |
| Harbor ATIF | 1.1× reduction835,187 | 1.7× reduction2,371,530 |
| Trajectory (untruncated) | 4.5× reduction211,923 | 2.0× reduction1,938,450 |
| Trajectory (default) | 5.6× reduction170,934 | 5.4× reduction727,516 |
Token counts measured with the Anthropic count-tokens API on real coding sessions.
Listing & formatting trajectories
The trajectory package lets you list sessions from any harness and convert them into normalized trajectories:
import { listTrajectories, normalizeTranscript } from "@letta-ai/trajectory";
import { readFileSync } from "fs";
// Discover local Claude Code sessions (also works for codex, letta-code, ...)
const page = await listTrajectories({ source: "claude-code", limit: 10 });
// Normalize one into the standard record format
const transcript = readFileSync(page.items[0].path, "utf8");
const { records, diagnostics } = normalizeTranscript({
source: "claude-code",
transcript,
});You can use the package to aggregate data across harnesses for indexing, or for processing with memory agents:
const sources = ["claude-code", "codex", "letta-code"] as const;
for (const source of sources) {
let cursor: string | undefined;
do {
const page = await listTrajectories({ source, limit: 100, cursor });
for (const item of page.items) {
const { records } = normalizeTranscript({
source,
transcript: readFileSync(item.path, "utf8"),
});
await index(source, item.id, records); // your indexing / memory pipeline
}
cursor = page.nextCursor;
} while (cursor);
}How we're using trajectory
Learning across agent harnesses in Letta Code
The Letta Code harness now uses the trajectory format to normalize session data from other local agents like Claude Code and Codex. Past trajectory data is reviewed to bootstrap the agent's memory. Agents can also search through trajectory files to find information from past sessions, even if they were from another harness.
Dreaming across harnesses
Letta Code's background "dreaming" process reviews recent sessions to consolidate what the agent learned into persistent memory. With the trajectory format, dreaming is no longer limited to Letta Code's own sessions: the dream process can select and normalize sessions from every harness on the machine, so lessons learned in a Claude Code or Codex session inform the agent's memory the same way its own sessions do.
Next steps
Try out the package:
npm install -g @letta-ai/trajectoryYou can also run /init in Letta Code to review experience from other harnesses:
npm install -g @letta-ai/letta-code
