Skip to content

feat(atif): add Harbor ATIF trajectory format support#11

Open
jfeldstein wants to merge 2 commits into
openai:mainfrom
jfeldstein:feat/harbor-atif-trajectories
Open

feat(atif): add Harbor ATIF trajectory format support#11
jfeldstein wants to merge 2 commits into
openai:mainfrom
jfeldstein:feat/harbor-atif-trajectories

Conversation

@jfeldstein
Copy link
Copy Markdown

@jfeldstein jfeldstein commented May 8, 2026

Currently

Euphony renders Harmony conversations and Codex JSONL session logs, with a dedicated <euphony-codex> viewer for the latter. Harbor's Agent Trajectory Interchange Format (ATIF) — a single-document JSON format that's structurally distinct from both — is not detected and falls through to the generic JSON viewer, which loses the trajectory-specific rendering (tool-call ↔ observation pairing, agent metadata, step metrics, etc.). Loading a *.trajectory.json from harbor/tests/golden/... today shows raw JSON with no conversational structure.

Change Summary

  • New src/utils/atif-trajectory.ts: schema types, isAtifTrajectory detector, parseAtifTrajectory (ATIF → Conversation), serializeAtifTrajectory round-trip, and validateAtifTrajectory. Steps map to one primary dialogue message plus optional reasoning, tool-call, and observation messages. Tool calls and their matching observations correlate by function_name (built from a per-step tool_call_id → function_name map) so the viewer renders the call/output pair under a single tool label.
  • New <euphony-atif> Lit shell that mirrors <euphony-codex> and delegates to <euphony-conversation>. Read-only by design (no is-editable property forwarded).
  • New src/components/app/local-data-parser.ts: extracted the inline parser out of local-data-worker.ts so it's unit-testable. ATIF detection runs before the Codex JSONL check.
  • src/components/app/app.ts: DataType.ATIF, state field, render branch, and URL-load branch.
  • Validator mirrors harbor's Pydantic constraints: sequential step_id, agent-only fields, ContentPart shape (text/image required fields, image media_type literal), tool-call ↔ observation source_call_id references, strict ISO-8601 timestamps, tool_definitions envelope shape.
  • Tests: src/utils/atif-trajectory.test.ts (12 cases) and src/components/app/local-data-parser.test.ts (6 cases) — both run under vitest.

Acceptance Criteria

  • pnpm test — vitest passes (18/18)
  • pnpm build — clean
  • Load a Harbor ATIF trajectory via URL (?path=https://...) → renders with Session/Agent/Schema/Steps chips and tool-call/output pairs sharing a label
  • Regular Conversation JSON still routes correctly (verified: examples/simple-harmony-convos.jsonldataType=conversation<euphony-conversation>)
  • Codex JSONL still routes correctly (verified: minimal session_meta/event_msg/response_item JSONL → dataType=codex<euphony-codex>; ATIF detection runs first but only matches single-element arrays whose first element looks like an ATIF document)
  • Round-trip integrity: serializeAtifTrajectory(parseAtifTrajectory(x).conversation) deep-equals x for all harbor goldens (8/8 terminus_2 fixtures + 2/2 openhands *.trajectory.json fixtures, all v1.5/v1.6, deep-equal)
  • Routing matrix: every *.trajectory.json in harbor/tests/golden/{terminus_2,openhands} routes to DataType.ATIF and mounts <euphony-atif> (10/10); both *.traces.json files (raw OpenHands replay events, not ATIF) correctly fall back to DataType.JSON
  • Load via "Load local file" — manually checked by me, Jordan

Notes

  • Read-only by design. <euphony-atif> does not expose is-editable, and ?disable-editing-mode-save-button=${true} is set as belt-and-suspenders. serializeAtifTrajectory returns the originally-parsed document verbatim — it does not synthesize ATIF from edits to the in-memory Conversation, because the schema requires sequential step IDs, agent name/version, and tool-call/observation ID references that arbitrary Harmony conversations don't reliably preserve. If lossy export of edited trajectories ever becomes a requirement, build a separate writer rather than changing this one.
  • Detection ordering. ATIF runs before Codex JSONL detection. ATIF is a single JSON document (one-element array after JSON.parse), so the discriminator is cheap and unambiguous (schema_version: "ATIF-*" + agent.{name,version} + non-empty steps). Putting it first prevents a future ATIF version with an event-shaped extra field from being misclassified as Codex.
  • Validator strictness vs. harbor. Harbor's Pydantic types tool_definitions only as list[dict[str, Any]] but the RFC text mandates the OpenAI {type, function} envelope; we enforce the RFC. Image media_type is restricted to the four-MIME Literal harbor uses. Strict ISO-8601 timestamp regex matches Step.validate_timestamp in harbor/src/harbor/models/trajectories/step.py; the parser uses the same check so a timestamp that fails validation also yields create_time === undefined rather than a Date.parse-coerced value.

jfeldstein added 2 commits May 8, 2026 11:50
Add Vitest + npm test script; cover atif-trajectory (validation, parse,
serialize, ISO timestamps, ContentPart rules) and local-data-parser
(dispatch table, ATIF-before-Codex guard). These tests fail until the
implementation commit.
Implement ATIF v1 schema/types, detection, parse/serialize/validate,
<euphony-atif> viewer, app and worker integration with extracted
local-data-parser for testability. Adds uv.lock.
@jfeldstein jfeldstein force-pushed the feat/harbor-atif-trajectories branch from 37a0f61 to 0d27cb7 Compare May 8, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant