Skip to main content

trajectory

Normalize agent transcripts from different runtimes into one validated, model-ready record format.

Agent tools represent the same concepts—messages, reasoning, tool calls, and tool results—in incompatible native formats. trajectory provides one TypeScript API that turns those formats into deterministic, structured records for training, evaluation, analysis, and inference.

The caller supplies a transcript string and its source. The one exception is Deep Agents, whose sessions normalizeCheckpoint reads from its local LangGraph SQLite store by thread ID; see src/adapters/deepagents/.

Installation

The TypeScript package is published as @letta-ai/trajectory:

npm install @letta-ai/trajectory

The Python wrapper is published as agent-trajectory and imports as trajectory:

pip install agent-trajectory

Quick start

import { normalizeTranscript } from "@letta-ai/trajectory";

const { records, diagnostics } = normalizeTranscript({
  source: "codex",
  transcript: rawJsonl,
});

records contains the normalized trajectory. diagnostics is always present and is empty when the transcript required no recoverable cleanup.

{
  "records": [
    { "role": "meta", "source": "codex" },
    {
      "role": "user",
      "content": "Check the current directory.",
      "timestamp": "2026-07-10T12:00:00.000Z"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_1",
          "name": "exec_command",
          "args": "{\"cmd\":\"pwd\"}"
        }
      ],
      "timestamp": "2026-07-10T12:00:01.000Z"
    },
    {
      "role": "tool",
      "tool_call_id": "call_1",
      "content": "/workspace",
      "timestamp": "2026-07-10T12:00:02.000Z"
    }
  ],
  "diagnostics": []
}

Supported sources

source Accepted input format Normalized meta.source
atif ATIF-v1.0 through ATIF-v1.7 whole-trajectory JSON atif
claude-code Native Claude Code JSONL claude-code
codex Native Codex rollout JSONL codex
copilot-cli Native GitHub Copilot CLI event JSONL copilot-cli
cursor Cursor role/message content-block JSONL capture cursor
droid Native Droid session JSONL droid
gemini-cli Native Gemini CLI whole-session JSON gemini-cli
hermes Session-store message-row array or a { "session": {...}, "messages": [...] } envelope hermes
letta-code Letta Code client transcript.jsonl letta-code
omp Native OMP (Oh My Pi) coding-agent session JSONL (pi-agent session format) omp
openclaw Native OpenClaw session JSONL (pi-agent session format) openclaw
opencode Native OpenCode { "info": ..., "messages": [...] } session JSON opencode
openhands JSON event array or an events-API { "items": [...] } envelope openhands
pi Native pi-coding-agent session JSONL pi
deepagents Deep Agents CLI LangGraph SQLite store plus threadId deepagents

Tool result records may include ok: boolean when the source exposes an authoritative structured outcome, such as Pi/OpenClaw isError, Claude Code is_error, Letta Code resultOk, OpenHands/Cursor is_error, OpenCode/Gemini terminal state, or Copilot CLI success. The field is omitted when the source does not expose a reliable status; result text is never interpreted as success or failure.

Each adapter lives in its own folder under src/adapters/ with a README documenting the exact input contract, decoding behavior, and what the adapter drops.

Listing local trajectories

listTrajectories() enumerates the sessions in a source's standard local store, newest first, with cursor pagination. It is a discovery layer beside normalization — normalizeTranscript() itself never touches the filesystem. ATIF, Copilot CLI, Cursor, Gemini CLI, and OpenCode are export-only input contracts and intentionally return listing_unavailable; callers locate and read the exports themselves.

import { listTrajectories } from "@letta-ai/trajectory";

let cursor: string | undefined;
do {
  const page = await listTrajectories({ source: "claude-code", limit: 100, cursor });
  for (const item of page.items) {
    // item.id, item.path, item.updatedAt?, item.title?, item.sizeBytes?
  }
  cursor = page.nextCursor;
} while (cursor);

Normalized records

A trajectory is an ordered array containing:

  • One leading meta record identifying the source and available session metadata.
  • Optional system message records when filters.systemMessages is explicitly set to "include"; system messages are omitted by default.
  • Generic observation records for environment feedback that cannot be attributed to one specific tool call, such as merged terminal output.
  • user and assistant prose records.
  • Optional reasoning records when the source exposes reasoning.
  • Assistant tool-call records with stable IDs and stringified JSON-object arguments.
  • tool records linked to earlier calls by tool_call_id.

Every conversational record has an ISO timestamp. The complete contract is available as both runtime validation and schema/trajectory-v1.schema.json.

The public function is:

normalizeTranscript(input: NormalizeInput): NormalizeResult

Adding a source

Each native format is implemented as a focused adapter that decodes source events into the shared internal message/tool contract. Common validation, linking, repair, timestamp handling, and bounds remain in the normalization core.

Use prompts/add-source.md with a coding agent to add a source from a local transcript corpus. The prompt covers privacy-safe corpus inspection, sanitized fixtures, compatibility checks, and the transcript-only API boundary.

Development

Requires Node.js 20+ and Bun for development:

bun install
bun run check

bun run check runs typechecking, the complete test suite, and the package build. It also regenerates the JavaScript runtime embedded in the Python wheel and fails if the committed bundle was stale. Run the Python parity suite with:

PYTHONPATH=python/src python3 -m unittest discover -s python/tests -v

See PARITY.md for compatibility checks performed against real transcript corpora and production source adapters. See SOURCE_VERSION_AUDIT.md for the privacy-safe source-version inventory, observed format families, and current decoder gaps.

License

Apache-2.0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_trajectory-0.3.0.tar.gz (40.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_trajectory-0.3.0-py3-none-any.whl (42.1 kB view details)

Uploaded Python 3

File details

Details for the file agent_trajectory-0.3.0.tar.gz.

File metadata

  • Download URL: agent_trajectory-0.3.0.tar.gz
  • Upload date:
  • Size: 40.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.21

File hashes

Hashes for agent_trajectory-0.3.0.tar.gz
Algorithm Hash digest
SHA256 c37b1fc04ab92faecd6f7d296b02759d0931ab156c42de397f2e365245a92027
MD5 0c0b53df4a582b6ac7aec2286d1b944c
BLAKE2b-256 4e3d5957ad09d7a57930164291957c31bee18ba8b87700b6d3fed982dbe3328f

See more details on using hashes here.

File details

Details for the file agent_trajectory-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_trajectory-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f8432fc5fb490b361ad07f71ec2bbd0fc7c63056bec1c78f4aa5db9867e37745
MD5 ff0ecd1efb93e74d561c71d0339ed26b
BLAKE2b-256 2602e9654cd6684f3f4889672a3f07368d19835a8a0918af31c35577c767a005

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page