RESEARCH — JUL 23, 2026

Trajectory: A Standard Format for Agent Experience Data

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:

  1. Standardized across harnesses, to avoid distraction from divergence in formats
  2. 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.

metawhich harness produced this trajectory
{
  "role": "meta",
  "source": "codex",
  "cwd": "/workspace",
  "git_branch": "main",
  "model": "gpt-5-codex"
}
usera user message
{
  "role": "user",
  "content": "Check the current directory.",
  "timestamp": "2026-07-10T12:00:00.000Z"
}
reasoningthe agent's thinking, when the harness exposes it
{
  "role": "reasoning",
  "content": "The user wants the working directory — I'll run pwd.",
  "timestamp": "2026-07-10T12:00:01.000Z"
}
assistantthe agent invokes a tool
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    { "id": "call_1", "name": "exec_command", "args": "{\"cmd\":\"pwd\"}" }
  ],
  "timestamp": "2026-07-10T12:00:02.000Z"
}
toolthe result, linked by tool_call_id
{
  "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 CodeCodex
Nativebaseline951,115baseline3,919,385
Harbor ATIF1.1× reduction835,1871.7× reduction2,371,530
Trajectory (untruncated)4.5× reduction211,9232.0× reduction1,938,450
Trajectory (default)5.6× reduction170,9345.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/trajectory

You can also run /init in Letta Code to review experience from other harnesses:

npm install -g @letta-ai/letta-code