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

Imported conversations (separate API)

Slack channels are multi-party conversations, not agent execution traces. They use an independent conversation schema and API:

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

const { records, diagnostics } = normalizeConversation({
  source: "slack",
  transcript: rawJsonl, // one channel's messages, as fetched
  channel: "C0AB…",
  channelName: "eng-deploys", // optional
  users, // optional users.list rows, for display names and bot flags
});

One channel is one conversation: a leading meta with a participants table, then top-level posts in time order with thread replies nested once under their root. Python exposes normalize_conversation from trajectory.conversations. The format does not extend NormalizedRecord, normalizeTranscript, or normalizeToCanonical; agent schemas and canonical schema version remain unchanged.

See CONVERSATIONS.md for the contract and Slack for thread assembly and supported content. Slack is the only conversation adapter in V0. Speaker names, reaction counts, and file placeholders are supported; recipients, file contents, and other message metadata remain deferred.

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.

Release files for agent-trajectory 0.4.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-trajectory 0.4.3
File Size Uploaded
agent_trajectory-0.4.3.tar.gz 46.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-trajectory 0.4.3
File Interpreter ABI Platform
agent_trajectory-0.4.3-py3-none-any.whl Python 3 none any Details

Total release size: 95.1 kB

Release files / agent_trajectory-0.4.3.tar.gz

Download URL agent_trajectory-0.4.3.tar.gz
Size 46.3 kB
Tags Source
SHA-256 checksum
How to use checksums
14e6a21a7ddb741ff395f7677b4a2a0f7209215dbf77696cd8a4bd5d165ee12e
BLAKE2b-256 checksum
How to use checksums
b7d076287a706ddeb7c419444493271b6fc600051b515b066f7d95d848db7eba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.21

Release files / agent_trajectory-0.4.3-py3-none-any.whl

Download URL agent_trajectory-0.4.3-py3-none-any.whl
Size 48.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5bf8c8f1b8568b698c26dd2ee56b15013cc0cbc3f6d8c389c737dcd23cbc116f
BLAKE2b-256 checksum
How to use checksums
7a84d9fd369b828f8e87406e2c2b2c4b9a3b338e83be976d3ed1fc64a10e16cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.21

Release history Release notifications | RSS feed

This release

0.4.3 This release

2 release files

0.3.0

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release 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