hypabolic-trajectory
Normalize coding-agent session transcripts into deterministic, versioned Trajectory contracts. Independent native Python 3.11+ runtime of the Hypabolic Trajectory product — same wire contracts and shared conformance suite as .NET, TypeScript, and Rust. No FFI to other language runtimes.
| PyPI name | hypabolic-trajectory (org Hypabolic) |
| Import root | hypabolic_trajectory |
| Requires-Python | >=3.11 |
| Normalizer contract | 0.2.0 (identity-stable; embedded in canonical output) |
| Wire package version | WIRE_PACKAGE_VERSION — tip pin "0.1.0" (must match peer runtimes + goldens on the same tag; not auto-bound to package SemVer) |
| License | MIT |
Published vs tree: NuGet / npm / crates
0.1.0already ship without AHP. The first public PyPI cut of this package ships on the next synchronized multi-registry tag (with tip sources including AHP Shape A). Until that tag, install from this monorepo (editable) or wait for the tag.
Package map
| Dist / extra | Import | Role |
|---|---|---|
hypabolic-trajectory (core wheel) |
hypabolic_trajectory |
Normalize, project (including pure project_otel_genai), listing, bundled contracts + runtime-capabilities.json, and hypabolic_trajectory.otel (SpanSetSink / emit_to, no SDK) |
hypabolic-trajectory[otel] |
same hypabolic_trajectory.otel |
Optional OpenTelemetry SDK sink helpers + opentelemetry-* deps only. Does not gate pure projection or emit_to with a pure sink. |
| Conformance runner | not published | python/tools/trajectory_conformance — stdin/stdout protocol v1 for conformance/verify.py |
| Sample CLI | not published | python/samples/trajectory_cli — browse/list/show (no console script) |
Cross-ecosystem map (same product):
| Ecosystem | Core | Optional |
|---|---|---|
| .NET | Hypabolic.Trajectory |
.OpenTelemetry, .Testing |
| TypeScript | @hypabolic/trajectory |
@hypabolic/trajectory-node, @hypabolic/trajectory-otel |
| Rust | hypabolic-trajectory |
hypabolic-trajectory-opentelemetry |
| Python | hypabolic-trajectory |
[otel] SDK sinks only |
Install
# After first public tag (replace with the tag SemVer):
pip install hypabolic-trajectory==<tag-semver>
pip install 'hypabolic-trajectory[otel]==<tag-semver>' # optional SDK sinks
# From this monorepo (development):
python -m pip install -e './python[dev]'
The published wheel has no console scripts. The conformance runner and sample CLI are monorepo-only.
Sample CLI (unpublished)
# After editable install of the core package:
PYTHONPATH=python/samples python -m trajectory_cli list --source pi
PYTHONPATH=python/samples python -m trajectory_cli show \
--source pi \
--path conformance/cases/pi/tool-calls/input.jsonl
PYTHONPATH=python/samples python -m trajectory_cli browse
See samples/trajectory_cli/README.md.
Supported public imports (semver-stable)
Only these paths are supported:
- Package root
hypabolic_trajectory— names in root__all__ hypabolic_trajectory.ir— multi-project IR surface (ir.__all__)hypabolic_trajectory.otel— always importable from the core wheel
Any other module path (api, engine, normalize, sources, …) is
unsupported and may break without notice.
from hypabolic_trajectory import (
# free functions
normalize_to_ir,
normalize_to_letta,
normalize_to_canonical,
normalize_to_hypabolic,
project_letta,
project_canonical,
project_hypabolic,
project_openai,
project_minimal_jsonl,
project_otel_genai,
list_trajectories,
serialize_projection,
canonical_json,
# engine
TrajectoryEngine,
# request / errors
NormalizeRequest,
SourceContext,
NormalizeOptions,
Bounds,
Filters,
TrajectoryError,
Diagnostic,
TrajectorySource,
# versions
NORMALIZER_CONTRACT_VERSION, # "0.2.0"
WIRE_PACKAGE_VERSION, # tip "0.1.0" until coordinated bump
PACKAGE_VERSION,
__version__,
)
from hypabolic_trajectory.ir import TrajectoryIR # also re-exported at root
from hypabolic_trajectory.otel import SpanSetSink, emit_to
OTEL import matrix
| Import / call | Without opentelemetry-* |
With [otel] extra |
|---|---|---|
from hypabolic_trajectory import project_otel_genai |
Succeeds | Succeeds |
from hypabolic_trajectory.otel import SpanSetSink, emit_to |
Succeeds | Succeeds |
emit_to(pure_sink, ir) |
pure project + sink.emit; no SDK |
Same |
| Concrete SDK helper symbols (if shipped) | ImportError with install hint |
Succeeds when deps present |
Whole hypabolic_trajectory.otel package |
Always in the core wheel | Same |
Quick usage
from pathlib import Path
from hypabolic_trajectory import (
NormalizeRequest,
TrajectorySource,
list_trajectories,
normalize_to_ir,
project_hypabolic,
project_letta,
project_otel_genai,
serialize_projection,
TrajectoryEngine,
)
# Listing always takes an explicit root (no home default in library APIs).
page = list_trajectories(
source=TrajectorySource.CLAUDE_CODE,
root=Path.home() / ".claude" / "projects",
limit=20,
)
session = page.items[0]
transcript = Path(session.path).read_bytes()
request = NormalizeRequest(
source=TrajectorySource.CLAUDE_CODE,
transcript=transcript,
)
ir = normalize_to_ir(request)
hypabolic = project_hypabolic(ir)
messages = project_letta(ir)
spans = project_otel_genai(ir) # pure; no OTEL SDK required
# Product JSON emit (shared Trajectory string-escape; never stdlib json.dumps
# for identity/product schemas):
wire = serialize_projection(hypabolic)
# Engine (tip matrix including pure otel); free functions ignore engine mutations.
engine = TrajectoryEngine.create_default()
out = engine.project(ir, "hypabolic-trajectory-v1")
Filters (normalize options)
NormalizeOptions.filters controls content inclusion during normalization:
| Field | Values | Default | Effect |
|---|---|---|---|
filters.tool_results |
"include" | "omit" |
"include" |
When "omit", tool-result record bodies are dropped while structure/diagnostics remain policy-correct |
Bounds (options.bounds.tool_arguments / tool_results) cap argument/result
character lengths with documented truncation strategies. Domain validation of
options runs at free-function / engine entry (DTO construction does not raise
TrajectoryError).
from hypabolic_trajectory import (
Filters,
NormalizeOptions,
NormalizeRequest,
TrajectorySource,
normalize_to_ir,
)
request = NormalizeRequest(
source=TrajectorySource.PI,
transcript=b"...",
options=NormalizeOptions(filters=Filters(tool_results="omit")),
)
ir = normalize_to_ir(request)
Dual timestamps
Source adapters and the normalizer preserve dual timing when present (never fabricated):
| Clock | Field | Role |
|---|---|---|
| Millisecond | timestamp_ms (int) |
Filled / synthesized body clock used for public message, canonical, Hypabolic, and jsonl-minimal timestamps |
| Precise | timestamp_precise (str, optional) |
Source-native high-resolution string when present; copied onto IR records and model invocations |
Public timestamp formats (filled timestamp_ms only — never source_timestamp_ms):
| Surface | Format |
|---|---|
| Message / canonical / Hypabolic | yyyy-MM-ddTHH:mm:ss.fffZ |
Listing updated_at |
Z form |
jsonl-minimal |
yyyy-MM-ddTHH:mm:ss.fff+00:00 (three fractional digits; omit key when filled ms is absent) |
| OTEL span bounds | precise as-is; else ms → seven-digit pad (Z → 0000+00:00) |
Identity formulas and string escape
Authority: contracts/spec/identity.md, contracts/spec/canonical-json.md, and
peer goldens. Digests are 64 lowercase hex.
| Product field | Formula |
|---|---|
Hypabolic trajectory_id |
sha256(utf8(compact_json([source_wire_name, group_id]))) |
Model-invocation id |
sha256(utf8(compact_json([group_id, identity, "model-invocation"]))) |
| Model-invocation absolute offset | checked decoded.source_offset + base_byte_offset (signed int64; overflow → invalid_input) |
segment.partial |
config.partial or base_byte_offset != 0 |
Trajectory string-escape (shared by canonical_json, serialize_projection,
and each project_minimal_jsonl line):
- Walk the string as UTF-16 code units.
- Short escapes for
",\\,\b,\t,\n,\f,\r. \uXXXXfour uppercase hex forU+0000–001F,U+E000–F8FF,U+2028,U+2029, and surrogates; else UTF-8.- Do not escape solidus
/; no Unicode normalization; no BOM.
Only identity hashing sorts object keys by UTF-16 code unit order via
canonical_json. Product serialize_projection preserves insertion order.
Capabilities and sources
Advertised surface is the tip matrix in
python/runtime-capabilities.json (must equal
contracts/compatibility.json and peer TS/Rust manifests at ship):
- Sources:
pi,claude-code,codex,openclaw,hermes,ahp(Shape A offline ChatState snapshot; listing Phase 3 stub) - Outputs:
letta-trajectory-v1,letta-canonical-v1,hypabolic-trajectory-v1,openai-chat-messages,jsonl-minimal,otel-genai-spans-v1 - Capabilities:
normalize,normalize-partial,list-explicit-root,typed-diagnostics,typed-fatal-errors,deterministic-rerun - Slice:
ML13(historical id; AHP is a post-0.1.0source addition on tip)
Do not treat IMPLEMENTED_SOURCES as a registry claim — the capabilities
file is authoritative for CI and advertising.
Conformance (filtered runner argv)
Protocol v1 runner (unpublished): python/tools/trajectory_conformance.
# Editable install + unit tests
python -m pip install -e './python[dev]'
python -m pytest python/tests -q
# Tip suite (unfiltered verify defaults to compatibility tip set):
python conformance/verify.py --repository-root . -- \
env PYTHONPATH=python/src:python/tools python -m trajectory_conformance
# Progressive / filtered argv from claimed capabilities (fail-closed when ⊂ tip):
python conformance/verify.py --repository-root . \
$(python tools/conformance_argv_from_capabilities.py --repository-root .) \
-- \
env PYTHONPATH=python/src:python/tools python -m trajectory_conformance
# Manual filters while iterating:
python conformance/verify.py --repository-root . \
--source pi --operation normalize-letta --operation normalize-canonical -- \
env PYTHONPATH=python/src:python/tools python -m trajectory_conformance
When claimed sources/outputs are a proper subset of tip, CI must pass
explicit --source / --operation filters (generator: tools/conformance_argv_from_capabilities.py).
Never run unfiltered verify while claimed ⊂ tip.
Identity baseline:
sha256sum --check conformance/identity-baseline.sha256
Version pins
| Symbol / field | Value / rule |
|---|---|
NORMALIZER_CONTRACT_VERSION |
"0.2.0" — wire identity contract |
WIRE_PACKAGE_VERSION |
"0.1.0" tip pin until all runtimes + goldens move together |
PACKAGE_VERSION / __version__ |
From installed dist metadata (stamp-synced with root VERSION) |
Canonical normalizer_version |
Contract version 0.2.0 (never package SemVer) |
Hypabolic normalizer.version |
WIRE_PACKAGE_VERSION |
OTEL instrumentation_version |
WIRE_PACKAGE_VERSION |
Development
From the monorepo root:
python -m pip install -e './python[dev]'
python -m pytest python/tests -q
Implementation authority: docs/python-implementation-spec.md,
contracts/, and conformance/. Status:
docs/python-impl-status.md.
License
MIT — Copyright 2026 Hypabolic
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hypabolic_trajectory-0.1.1.tar.gz.
File metadata
- Download URL: hypabolic_trajectory-0.1.1.tar.gz
- Upload date:
- Size: 74.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
704311efc598bd7b72aaae7fa9b96a9b049301d5a47304156714396255aad2aa
|
|
| MD5 |
07bad90969b862ec079b14ce12be41c1
|
|
| BLAKE2b-256 |
3f11510f6f1fec2e12dce353a1c5a3e7772bbdd4ccc04df911db9efdac9420e9
|
Provenance
The following attestation bundles were made for hypabolic_trajectory-0.1.1.tar.gz:
Publisher:
release.yml on Hypabolic/Trajectory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hypabolic_trajectory-0.1.1.tar.gz -
Subject digest:
704311efc598bd7b72aaae7fa9b96a9b049301d5a47304156714396255aad2aa - Sigstore transparency entry: 2341593557
- Sigstore integration time:
-
Permalink:
Hypabolic/Trajectory@1d9d55aa3a0ce3f82c9c74beff5cd41c2f558763 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Hypabolic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1d9d55aa3a0ce3f82c9c74beff5cd41c2f558763 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hypabolic_trajectory-0.1.1-py3-none-any.whl.
File metadata
- Download URL: hypabolic_trajectory-0.1.1-py3-none-any.whl
- Upload date:
- Size: 108.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
407c25343d4d3c85cf927ca4e1063d0c09443cc022f416da9b18f013690f914a
|
|
| MD5 |
5f3bd4dadd97c57963272eeb39522153
|
|
| BLAKE2b-256 |
3df36884abc03bff3d5441e153f86089812b048f06878154832c801104a11b64
|
Provenance
The following attestation bundles were made for hypabolic_trajectory-0.1.1-py3-none-any.whl:
Publisher:
release.yml on Hypabolic/Trajectory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hypabolic_trajectory-0.1.1-py3-none-any.whl -
Subject digest:
407c25343d4d3c85cf927ca4e1063d0c09443cc022f416da9b18f013690f914a - Sigstore transparency entry: 2341593561
- Sigstore integration time:
-
Permalink:
Hypabolic/Trajectory@1d9d55aa3a0ce3f82c9c74beff5cd41c2f558763 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Hypabolic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1d9d55aa3a0ce3f82c9c74beff5cd41c2f558763 -
Trigger Event:
push
-
Statement type: