Skip to content

Session file format

Persyk’s repository is a directory of self-contained file-based sessions. There is no database or conversion layer for prior storage. Existing user files are not rewritten automatically.

Identity and layout

<session-id>/
session.jsonl
assets/
<recording-id>.wav
protocol-<uuid>.bin
  • SessionId: durable identity in the header, independent of directory spelling.
  • ContinuationId: local selected-leaf state, not a persisted global registry.
  • RecordingId: one audio capture period and its unique asset.
  • ConnectionId: one provider connection; remote item IDs are scoped to it.
  • MessageId: canonical local message entry ID, never a provider item ID.

The header and recording identity are durable before capture. A user-only session is valid without an assistant response. This exact example is checked against packages/session-model/fixtures/user-only.jsonl:

{"type":"session","version":1,"id":"00000000-0000-4000-8000-000000000002","timestamp":"2026-01-01T00:00:00.000Z"}
{"type":"message","id":"user-message-1","parentId":null,"timestamp":"2026-01-01T00:00:01.000Z","message":{"role":"user","timestamp":1767225601000,"content":[{"type":"text","text":"User-only finalized transcription"}]}}

Version 1 contract

The authoritative schemas are packages/session-model/src/schema.ts; native validation and shared golden/rejection fixtures verify the same contract. The header has only type: "session", version: 1, id and timestamp. Every subsequent entry has {type,id,parentId,timestamp} plus its type-specific fields. IDs match [A-Za-z0-9][A-Za-z0-9._-]{0,127}; new writers use collision-resistant full IDs. Parents reference an earlier committed entry or null. Envelope timestamps are ISO 8601 with a timezone; message timestamps are nonnegative epoch milliseconds.

Entry typeFields beyond the common envelopeConversation effect
messagemessageFinalized canonical message
customcustomType, dataBranch-linked state, not context
custom_messagecustomType, content, display, optional detailsExplicit conversation content
session_infoOptional name, archivedGlobal display/archive metadata
labeltargetId, nullable labelAnnotation, not context
model_changeprovider, modelId, optional apiConfiguration provenance
api_connectionconnectionId, provider, endpoint, requestedModel, api, originIdNon-secret connection provenance
realtime_eventConnection, observation and sanitized payload fieldsEvidence only
recording.startedrecordingId, assetCapture lifecycle
recording.finishedrecordingId, durationMs, outcome, optional gapsComplete/incomplete outcome
recording.interruptedrecordingId, reason, optional durationMsExplicit interruption outcome
message.removedAncestor canonical/custom-message targetIdBranch-local exclusion

Messages have role user, assistant or toolResult, typed content and timestamp. Optional fields retain non-secret provenance, usage, replay metadata, stop reason and error message. Tool results also require toolCallId, toolName and isError; arbitrary tool details do not enter context.

Known content types are text, image, thinking, toolCall and audio. Unknown blocks round-trip unchanged; malformed known blocks fail. Audio/image blocks reference assets; audio may include recording identity and a meaningful millisecond range. Available assets include a confined assets/<name> path, MIME type and optional byte range, encoding and SHA-256. Missing assets carry a reason and optional source metadata; their absence does not erase saved text.

Records are newline-terminated strict UTF-8 JSON, at most 1 MiB each and 128 nested containers. Keys/strings must be Unicode scalar text; valid surrogate pairs are supported. Unknown top-level semantics, extra envelope fields, unsupported versions, missing parents and invalid removals block authoritative projection and mutation. Unknown custom namespaces and nested provider event types remain readable. Diagnostics never include message bodies or secret values.

Branches and context

Omitting a leaf selects the last appended entry of any type; explicit null selects empty ancestry. An absent explicit ID fails rather than substituting the default. Opening creates no entry or branch. Each continuation advances only after its own acknowledged append; sibling writers never silently rebase it.

Conversation reconstruction follows selected ancestry and applies removals. Custom state, recording/protocol observations and configuration are not messages. Custom message details remain separate from content. Session name/archive metadata resolve globally, while custom state is branch-local.

Effective history, generic context conversion and target-API validation are separate layers. Unsupported target content must not break history reads. Orphan tool results may be omitted only at target conversion, without deleting them from history. Historical API events are never replay commands.

Native writes, assets and recovery

Opaque native handles authorize configured roots and validated sessions/assets; webviews cannot authorize arbitrary paths. Reject traversal and escaping symlinks, revalidate file identity and use exclusive creation rather than truncating assets.

A short-lived OS advisory lock plus in-process serialization protects committed validation, append and sync. Preserve supplied parents. Identical stable-ID retries return the committed record; conflicting content fails. Batches can commit a prefix, so retry the same IDs/content instead of assuming atomicity. Appends accept up to 256 records/4 MiB per batch. Paged reads are bounded and native scanning retains an entry index rather than entire audio or protocol bodies.

Read/open never repairs. Explicit recovery preserves a genuinely incomplete trailing fragment before truncating it, or adds the missing newline to an otherwise valid record. Malformed complete/interior data, definite syntax errors and resource limits are not tail-repair candidates.

Each capture owns an OS asset lock. Another live capture cannot be repaired or deleted. Interrupted-WAV recovery preserves samples, fixes sizes and records an interruption; it never reconstructs canonical messages from events. Operations that need both locks acquire asset ownership before the session mutation lock. Confirmed whole-session deletion stops local capture, rejects external live capture, revokes stale handles and stages outside discovery before retryable cleanup. Other directories and copies are untouched.

sessions.directory optionally sets an absolute write root. Otherwise use Documents/persyk/sessions in production and Documents/persyk-dev/sessions in development. sessions.additionalDirectories lists other absolute discovery roots. Changing roots does not move files. A missing default folder produces an empty picker; the first recording creates it. Explicit missing roots and genuine access errors remain diagnostics.

Search direct-child sessions, deduplicate canonical locations and report distinct locations with the same header ID. Search is case-insensitive literal substring matching over current labels and surviving default canonical/custom-message text. Protocol/custom-state/provisional text, removed content and sibling branches do not match. Fallback labels and excerpts are derived from surviving content. Preserve created-time sorting and bounded requests. Results pin location, identity and leaf; external appends cannot silently change what a selected result opens. Refresh from files; no persistent catalog is required.

Realtime retention and ordered publication

Sanitize credentials, authorization, ephemeral secrets and credential-bearing URLs before persistence or diagnostics. Each realtime_event records connection identity, direction, observation, observedAt, sequence, payload and optional remote event ID, redaction paths and media references. Observations are send_attempt, send_failed or received; an outgoing attempt is not acknowledgement.

Externalized media references retain exact byte ranges/encoding. Write and sync bytes before acknowledging their references. Capture WAV ranges may be reused only when byte-identical; otherwise retain distinct assets. Live input chunks carry local WAV offsets through startup buffering and wait for their capture write before exact native verification. Matching input retains WAV ranges, not duplicate .bin files. Provider output, mismatched bytes and unavailable captures still use separate protocol assets. Existing sessions are not rewritten; do not manually delete their referenced assets.

Order canonical messages from provider predecessor/speech order, not completion arrival. B completed before A stays provisional until A resolves. Explicit failures record gaps rather than fabricated text. Assistants become canonical at response.done, not merely text completion. Repeated completions remain evidence without duplicate messages; late completions after closed gaps cannot reorder or resurrect history.

The observation queue is bounded to 512 jobs/8 MiB, ordered work to 256 items/ 4 MiB text. Persistence/backpressure failure visibly halts capture. Ordering limits enter finishing rather than silently dropping required observations. Stop has a 10-second realtime/deletion drain deadline, including requested/streaming assistant responses; filesystem operations still must settle. Close unresolved gaps, publish available finalized messages and finalize the same recording. Restart reports unfinished/unmaterialized work without generating missing messages.

Canonical completed user/assistant removal awaits provider acknowledgement, then appends message.removed. Explicit rejection leaves content unchanged. Timeout or transport uncertainty closes the connection and stops capture; local acknowledged history remains authoritative with an incomplete outcome. A late acknowledgement on the closed connection is evidence only. Remote success/local persistence failure retains an intent for retry without resending a successful remote delete.

Removal retains historical text/audio. Provisional items are labeled not yet saved and cannot be copied/removed as canonical content. Empty/all-removed conversations remain valid saved sessions. Only explicit discard deletes the newly created current session. Clipboard/notification effects use surviving canonical content and do not control persistence success.

Attribution and boundaries

Pi-aligned vocabulary and selected tree/context code/tests are adapted from revision 107d79f11072bbc8a3a757ed7fd69596bee7d68c; see THIRD_PARTY_NOTICES.md for the MIT notice and differences. No coding-agent runtime dependency is included.

Storage support does not imply tool/modality execution. An extension loader, API replay, saved-session connection restoration, picker/branch UI, automatic crash-time message reconstruction, physical per-message erasure and distributed/manual editing conflict resolution are outside this implementation.

See session verification for automated coverage, representative performance measurements and the manual macOS checks still unperformed.