Repository navigation
feat: build SessionKit as a Go library for safe native session handoff #1
Description
Summary
Build SessionKit, a reusable Go library for moving a coding agent's saved conversation between execution environments without silently losing supported context or copying credentials.
Start with Codex-to-Codex cold handoff. Design the library so Claude Code, OpenCode, and Copilot can be added later, but do not implement all of them in this issue.
The user-facing command belongs in the Orka CLI, under orka session migrate. SessionKit is the shared library behind that workflow, not another standalone CLI or service. Vekil can use the library at its launcher boundary and continue to supply provider configuration and credentials.
Problem: why this is needed
A developer may start a coding task locally, move the work into Orka, and later bring it back to their laptop. They want the agent to continue from the conversation it actually saved, including supported tool calls, results, and context.
Today, these are different things:
- Moving the Git checkout moves files, not the agent's conversation.
- Replaying visible chat messages can omit useful native state or change the effective context after compaction.
- Copying an entire agent home directory can copy credentials, settings, unrelated conversations, stale paths, and retained permissions.
- A command that successfully writes a file has not necessarily produced a session the destination agent can resume.
We need a small component that can answer: What state belongs to this session? Can the destination restore it? What exactly will change? Did installation complete?
If it cannot establish a supported handoff, it must explain the blocker instead of silently producing a lower-fidelity conversation.
Example workflow
- A developer finishes a local Codex turn and stops that instance.
- The Orka CLI uses SessionKit to inspect and package the selected native session.
- Orka authorizes the import, prepares the matching repository workspace, and uses SessionKit to install the supported native state into a fresh runtime.
- The runtime starts with fresh Orka credentials and current policy, then accepts a new authorized prompt.
- Later, the Orka CLI exports the continued session back to the laptop. The developer can resume it locally, including through Vekil.
This is the intended workflow, not a command implemented today. For the last step, Vekil already supports forwarding native resume arguments, for example:
vekil launch codex -- resume <restored-native-session-id>A normal same-machine resume through Vekil does not require SessionKit. The new value is moving and validating saved session state across environments.
Terms used in this issue
| Term | Meaning |
|---|---|
| Harness | The coding-agent program, such as Codex CLI or Claude Code. This is separate from the model/provider it calls. |
| Native session | The conversation records and related data saved by that harness. A native session ID alone is not the saved data. |
| Rollout / JSONL | Codex calls its saved conversation log a rollout. JSONL stores one JSON object per line. |
| Compaction | Replacing or shortening earlier context so the agent can continue within its context limit. Preserving old visible text is not always equivalent to preserving the resulting context. |
| MCP | The protocol a harness can use to connect to external tools. Its connections and credentials are not portable conversation data. |
| Cold handoff | Stop or safely settle the source, capture saved data, and start a fresh destination process. Running process memory is not transferred. |
| Workspace | The repository files the agent works on, including its revision and selected changes. |
| Bundle | A private package containing selected native data and a manifest describing its files, identities, versions, and integrity. |
| Native-preserving | Preserve the supported saved context, except for explicitly validated relocation changes. It does not mean copying credentials, permissions, or every file in the agent home. |
| Portable rewrite | Create a new conversation from a reduced, normalized history. This may be useful later, but it is a different promise from native handoff. |
Keep the source native session ID, destination native session ID, and Orka Session identity distinct. Importing a native ID must never grant ownership of an Orka Session.
Decisions already made
- Repository/module:
github.com/orka-agents/sessionkit. - Implementation language: Go. Do not make the production library depend on Python or on the
session-migrateexecutable. - Deliver a reusable library first. Do not add a SessionKit executable, daemon, database service, UI, or persistent session catalog.
- Orka owns the user-facing migration command and cluster operations.
- Vekil remains responsible for provider selection, fresh provider configuration, credentials, and launching a local harness.
- Vekil's existing Responses provider-failover engine stays separate. It switches providers while the local client and files remain in place; it is not native-session transfer.
- SessionKit must not import Orka/Vekil internal packages, Kubernetes clients, or application-specific authorization rules.
- No silent fallback from native handoff to portable rewrite or transcript-only reconstruction.
- Preserve the source. Do not delete it, overwrite another destination session, or implement a force-overwrite option.
Fast starting point
The first vertical slice is one fixture -> inspect -> capture/bundle -> plan -> install -> native resume -> return transfer. Start with that path, not a catalog or adapters for every harness. The reading path below links directly to the relevant source files.
First delivery: a small, useful supported subset
Implement and document one tested Codex-to-Codex profile:
- The
codex/paginated@0.160.0profile, with Codex 0.160.0 at both source and destination. Orka's prerequisite Codex/ACP pin update is merged. - One completed, self-contained paginated rollout containing conversation messages and at least one completed tool call/result pair. Legacy Codex rollouts are outside this delivery.
- A stopped source with no in-flight turn, pending permission, active child agent, or autonomous background work.
- One repository workspace supplied by the caller, with explicit source/destination working-directory information.
- A fresh, private destination session location.
- A complete local source -> relocated destination -> local return test using the supported native client.
Preserve supported opaque native context as bytes where required. Do not discard it merely because the library cannot interpret it. If the chosen format/profile cannot preserve and safely reload it, reject that case explicitly.
Initially reject unsupported compaction layouts, external history ancestry such as history_base, subagent projections, missing referenced files, and unverified format/version combinations. Do not claim support just because JSON parsing succeeds.
The supported subset must be documented precisely. A clear refusal is an acceptable result for an unsupported session; a success message after silently dropping context is not.
Implementation boundary
This issue covers the SessionKit library, its documented integration contract, and its tests. Orka CLI/controller wiring and any Vekil launcher changes should be separate linked consumer work. Do not claim that orka session migrate works until that integration has its own end-to-end validation.
The initial library must still expose enough information for those consumers to authorize, install, and resume safely. Avoid designing it around a standalone local command that owns all policy decisions.
What to learn from session-migrate, and what not to copy
The research reference is xhluca/session-migrate v0.11.0, inspected at commit c23b1dbd21404f78be3b69d42ff4fb158ff52105.
Borrow these ideas:
- Bounded format detection and parsing, with source-record provenance.
- Separate inspection, artifact construction, and native installation.
- Explicit reports for unsupported or transformed data.
- Private, no-clobber publication: create a new file without replacing an existing one, plus explicit collision checks.
- Native-client compatibility tests using sanitized fixtures and deterministic local providers.
Do not port its central conversion pipeline unchanged. It deliberately sends even same-harness transfers through a reduced event model and creates a new portable conversation. Some native graph, compaction, reasoning, and execution metadata is removed. That is appropriate for its portable-rewrite contract, not the authoritative representation for our native-preserving path.
Also avoid treating these as equivalent:
- An atomic file write and an atomic multi-file installation.
- A matching file timestamp and a consistent source snapshot.
- An import subprocess returning an error and proof that nothing was installed.
- A session appearing in a picker and proof that it can continue correctly.
- A metadata-only report and data safe to publish in unrestricted logs.
If implementation code or fixtures are reused, retain the applicable upstream license notices and record the source revision. Prefer small reviewed pieces and independently specified behavior over a mechanical translation.
Suggested implementation steps
1. Establish the library contract and a fixture before adding breadth
Create the minimal Go module and README. Select a Go version compatible with the intended consumers.
Document a small set of operations equivalent to:
- Inspect a selected source and report identity, format, dependencies, and compatibility.
- Capture/package a supported source into a private, verifiable bundle.
- Plan restoration for an explicit destination without writing anything.
- Install the already-validated plan without clobbering existing state.
- Verify installation and return the native identity and resume requirements.
These are responsibilities, not mandated API names. Keep the API small and use typed results/errors. The caller supplies source roots, destination roots, workspace information, and trusted destination settings. Do not infer production policy from the current process environment.
Start from a sanitized fixture produced by the selected Codex binary. Include ordinary messages and a unique completed tool result. Write expected preservation and rejection behavior before the installer.
Keep the normal development loop simple: go test ./... and go vet ./.... Document a separate command for the pinned native-client gate so an implementer can run the same check locally and in CI.
2. Capture a consistent source and describe every required component
- Require a quiescent source. Source metadata checks can detect races; they do not replace the caller's responsibility to stop or freeze writers.
- Copy the selected data into a bounded private snapshot, hash the bytes being copied, and parse/package that same snapshot.
- Include a bundle-format version, native format identifier, source writer version, adapter version, native identity, workspace expectations, and per-component logical path, role, size, and digest.
- Keep logical bundle paths separate from machine-local absolute paths.
- Validate all dependencies. Never follow arbitrary paths outside the approved source boundary.
- Preserve supported native records and relationships. A normalized inspection view may be derived from them, but must not become the only retained representation.
- Treat bundles as sensitive. Do not include authentication files, environment dumps, global configuration, unrelated sessions, or source permissions as active destination policy.
A copied conversation may already contain secrets in messages or tool output. Excluding credential files is not a guarantee that the bundle contains no secrets. Do not claim automatic redaction or silently redact data while claiming native preservation.
3. Make restoration planning explicit
A plan should freeze the selected source digest, target identity/layout, compatibility profile, and allowed changes before installation starts.
- Check actual supported formats/versions; changing a version label must not select or imply another writer implementation.
- Validate the destination workspace and working-directory mapping.
- Rewrite only supported structured relocation fields. Never globally replace paths inside conversation text or tool output.
- Keep destination credentials, MCP connections, approval policy, and provider configuration caller-owned. Do not activate an old Vekil loopback endpoint or token from saved metadata.
- Report preserved, transformed, synthesized, omitted, or unresolved data separately. Native mode must reject changes outside its preservation contract.
- Reject ambiguous identities, incomplete tool relationships, unresolved dependencies, and unsafe destinations before publication.
Do not promise arbitrary model/provider switching just because the source and destination use the same harness.
4. Install without losing track of partial outcomes
- Use caller-selected private roots, directory-relative path operations, and an explicit symlink/ownership policy.
- Do not overwrite an existing session or change permissions on unrelated existing directories.
- Use private files/directories and no-replace publication. Flush files and relevant directories where supported.
- Stage complete directory-based artifacts before making them discoverable when the native layout allows it.
- Record the operation's target identity and bundle digest before irreversible work. Preserve that identity across retries.
- Distinguish rejected before mutation, confirmed installed, and outcome unknown.
- A timeout, nonzero native-importer exit, or crash after publication requires reconciliation, not blind retry with a new identity.
- Return enough private evidence for the caller to reconcile. Keep public status separate from detailed provenance.
Use the smallest durable receipt/journal needed for this operation. Do not build a general workflow engine. Future harness adapters may need official native importers or database transactions; do not assume every harness is a single JSONL file.
SessionKit must not start a model turn or execute historical tools as a side effect of inspection, capture, or installation. Orka/Vekil own launching and authorizing the resumed client.
5. Build evidence in layers
Keep these checks distinct:
- The source parses and its dependencies are understood.
- The bundle preserves the declared state and passes integrity checks.
- Installed bytes/layout match the approved plan.
- The exact native client can cold-load the installed conversation.
- A new authorized turn receives the expected history and persists a new reply.
- A second restart and return transfer preserve that newly added history.
For automated native tests, use isolated homes, dummy credentials, an exact version/digest-pinned client, and a deterministic localhost provider. No personal credentials, private transcripts, or paid model requests should be required by the default validation path. Any required test client download must be explicit and pinned.
Assert semantic details, not just record counts or substring presence: roles, ordering, multiplicity, tool names/IDs/arguments/results, and exact opaque/media bytes where supported. The new reply must appear in newly appended native state, not merely somewhere in the final file.
Test format compatibility separately from authorization. A test using sandbox/approval bypass flags does not prove Orka's real permission boundary. Consumer integration tests must later cover current policy, fresh credentials, forbidden egress, controller restart, cancellation, and duplicate-operation prevention.
A dedicated native-client test gate must fail when its required client/tests are missing or skipped. Ordinary unit tests should remain easy to run without an Orka cluster.
Go-specific implementation notes
- Do not decode arbitrary native numbers through
float64and silently lose precision. Use deliberate numeric handling and retain raw JSON where fidelity requires it. - Specify duplicate-key, invalid Unicode, nesting-depth, node-count, and incomplete-final-record behavior. Decoder success alone is not a preservation guarantee.
- Configure explicit JSONL record limits; default scanner limits may be too small for tool output.
- Apply one operation-wide budget to input, decoded data, output expansion, temporary disk, subprocess output, and elapsed time. Enforce limits before or during allocation, not only afterward.
- Use cancellation-aware operations and content-safe errors. Errors may identify a component or record ordinal, but must not dump conversation bodies, tool arguments, tokens, or native-client stderr indiscriminately.
- Prefer a small harness adapter boundary over a large central switch that mixes parsing, model defaults, filesystem writes, and process launch.
Acceptance criteria
- The repository contains an independently usable Go module with no production Python dependency and no Orka/Vekil/Kubernetes dependency.
- There is no standalone SessionKit CLI, service, UI, persistent catalog, or unrelated harness implementation in the initial delivery.
- The README explains the problem, terms, supported Codex profile, limitations, and intended Orka/Vekil integration.
- Inspection and restoration planning make no changes and do not invoke models/tools.
- Capture hashes and packages the same selected snapshot, with an integrity inventory covering every required component.
- A supported fixture retains its declared native conversation and tool context through a relocated cold round trip.
- Unsupported versions, compaction/lineage, missing dependencies, malformed input, and unsupported active state fail clearly without silently reducing history.
- Source data is unchanged on success and failure. Existing destination state and another writer's replacement are preserved.
- Installation has explicit partial/unknown-outcome behavior and retry-safe target identity.
- Credentials and active authorization/configuration are not imported from the source; public results do not expose private native payloads.
- Tests cover payload corruption, duplicate keys, large records, broken linkage, source mutation, collisions, path/symlink boundaries, concurrent installation, and failure at publication/finalization boundaries.
- Exact-client tests demonstrate cold loading, correctly ordered historical context, a newly persisted reply, another restart, and the return transfer.
- Dedicated native validation cannot pass by skipping its required tests.
- Follow-up Orka CLI/controller and optional Vekil launcher work is documented, with no claim that the public integration already exists.
Explicit non-goals
- Moving running processes, RAM, terminals, sockets, or pending tool/permission requests.
- Live bidirectional synchronization or merging diverged conversations.
- Cross-harness conversion in the first delivery.
- Supporting all eighteen formats from
session-migrate. - Copying entire homes/databases or migrating authentication.
- Automatically replaying an old prompt that may already have had effects.
- Cloning repositories, resolving dirty-worktree conflicts, pushing commits, publishing PRs, or deploying Orka.
- Changing Vekil's provider-failover semantics.
- Silently salvaging damaged history or downgrading native handoff into transcript reconstruction.
Future cross-harness conversion
SessionKit's longer-term design should support two distinct paths:
| Path | Example | Contract |
|---|---|---|
| Native handoff | Local Codex -> Codex in Orka | Preserve supported native saved state, with explicit compatibility checks. |
| Cross-harness conversion | Codex -> Claude, or Claude -> Codex | Create a new destination conversation from transferable history, with explicit transformation and loss reporting. |
This section does not expand the first delivery. The initial milestone remains the verified native Codex round trip described above. Do not implement a universal conversation model or cross-harness converters merely to anticipate future work.
Keep these boundaries in the design:
- Preserve the original native bundle independently of any derived portable conversation representation. A conversion must not replace the only native copy.
- Give a converted conversation its own destination identity and report what was preserved, transformed, synthesized, omitted, or unsupported. Do not imply that a cross-harness round trip can recover information lost during conversion.
- Never silently fall back from failed native restoration to lossy conversion or transcript-only reconstruction.
- Keep destination credentials, tools, hooks, permissions, and provider configuration caller-owned. Historical tool calls provide context; they must not be re-executed during conversion.
- Validate each supported source/target format and version pair separately. Parsing or writing a native-looking file is not proof that the target can continue correctly.
- Track Codex <-> Claude conversion as a separate follow-up issue. It is a reasonable first conversion pair, not an acceptance requirement for this issue. Other harness pairs can follow the same explicit validation process.
Start here: a short reading and implementation path
The repository is currently empty. Avoid spending the first iteration designing every harness or a large extension framework.
- Read the upstream architecture, especially same-format rewriting and native installation.
- Read the small event model and same-format conversion. Understand why this cannot be our only preservation representation.
- Read the bounded reader/private writer and exact-client continuation test.
- Establish one pinned Codex fixture and its expected behavior. Implement inspection and rejection tests first, then capture/bundle, then planning and installation.
- Add the native cold-resume test before broadening the supported format. Finish the return path and fault-injection cases before starting another harness.
Related Orka work
- Orka issue #587 explains native conversation restoration.
- Orka PR #590, checked October 2, 2026, is draft and unmerged. Its implementation is OpenCode-only, read-only, and tied to an existing Orka-owned Session/workspace. Do not assume it is on main or that it already supports external imports.
- Its private snapshot contract and ACP resume/load handling are useful consumer references.
- Its OpenCode capture/restore codec illustrates strict selection and rejection, but assumes an isolated native store rather than a developer's shared home.
- Orka's pinned harness versions are the starting compatibility reference. At that baseline Codex is 0.145.0. Source labels and deployed binary versions must still be verified.
Related Vekil work
- Launcher contract.
- Codex provider configuration and forwarded resume arguments.
- Provider conversation failover, which remains separate from SessionKit.
Native Codex contract
- Pinned native resume parameters. Prefer ordinary native-ID resume. The raw-history/path overrides are explicitly experimental and must not be treated as a stable general migration API.
Definition of done
A developer can use the Go library to inspect a documented supported Codex session, capture its selected native state, install it at a verified destination, continue through an exact native client, and bring the continued state back. The process preserves its declared context, never copies source authority, never overwrites unrelated state, and reports unsupported or uncertain outcomes honestly.
Activity