docs blog library
github

Remote handoff snapshot

createDetachSnapshot in snapshot.ts writes a directory containing manifest.json and files/<relativePath> for each manifest entry. The manifest records byte length and SHA-256 for every file. An importer checks the complete bundle before it clones or writes any destination file.

The snapshot contains the conversation transcript, its per-conversation work directory and todo file, one Git bundle of HEAD, and tracked plus non-ignored untracked workspace files. It also carries the selected agent definition with llm.apiKeys removed (custom tools included), every user skill under $JAZZ_HOME/skills (a top-level symlinked skill is copied from its target), and the agent’s custom persona directory. It does not copy global Jazz config, credentials, plugin skills or personas, memory, or generated files. A persona that is neither built in nor under $JAZZ_HOME/personas, and transcripts referencing generated artifacts, stop export. A workspace must be a Git repository with a commit; ignored files and paths outside its root stay on the source machine. The Git bundle contains commit history, which may include files no longer present in the working tree.

prepareDetach in ownership.ts records a durable local fence. Both preparing and remote reject a new local agent run or conversation save. Snapshot creation must finish before preparation when it is given in-memory history, because it strictly saves that history first. A failed transfer may call abortDetach only while preparing. A committed handoff cannot be rolled back by losing SSH connectivity; the remote may be running. The remote host imports into its own Jazz home and a dedicated empty workspace, then starts a continuation under the same (agentId, conversationId). The source machine owns the agent definition and the conversation’s work directory, so a later handoff of either replaces the host’s copy. Skills and personas are shared by every handoff on a host: an identical copy is skipped and a different one stops the import.

applyDetachResult brings a released result home. It compares the handed-off workspace, the result, and the local tree: a path changed remotely whose local bytes match neither side is a conflict, and any conflict stops the apply before a write unless the caller lets the remote win. It then replaces the work directory and todos and saves the returned transcript through saveConversation(..., { fenceHeldBy }), which writes past the fence only for the handoff that holds it. A saved UI transcript stops at the handoff, so the remote turns are appended to it; if the remote rewrote history, the stale UI transcript is dropped and resume paints from messages.

The remote run persists its transcript without the work-state preamble it prepends each turn, so replies never stack preamble copies and reclaim sees the handed-off messages as a prefix.

machine-readable: /docs/maintainers/detach-snapshots.md · /llms.txt · /llms-full.txt