How Session Capture Works in Sandcastle Using `hostSessionStore`

Sandcastle captures agent sessions by creating dual SessionStore instances—one backed by the sandbox filesystem and one by the host—then uses transferSession to copy JSON-L session data from the sandbox to the host while rewriting absolute paths to match the host's working directory.

When running Claude Code agents inside Docker or Podman sandboxes, Sandcastle persists conversation history to the host filesystem through a structured capture mechanism. The hostSessionStore function serves as the primary interface for host-side storage, managing JSON-L files under ~/.claude/projects/<encoded-cwd>/ according to the implementation in src/SessionStore.ts.

Core Components of the Session Capture System

The session capture architecture relies on three main components defined in src/SessionStore.ts that coordinate file movement across the sandbox boundary.

hostSessionStore – Host-Backed Persistence

Located at lines 57-76 of src/SessionStore.ts, hostSessionStore returns a SessionStore interface that reads and writes session JSON-L files directly to the host filesystem. It organizes sessions under ~/.claude/projects/<encoded-cwd>/, where <encoded-cwd> represents a filesystem-safe encoding of the current working directory path. This store serves as the canonical location for all captured sessions, making them available for later resumption, inspection, or debugging.

sandboxSessionStore – Sandbox-Backed Writing

Defined at lines 91-134 of src/SessionStore.ts, sandboxSessionStore creates a store that operates within the sandbox environment. Unlike the host store that uses direct filesystem calls, this implementation uses a BindMountSandboxHandle with copyFileIn and copyFileOut methods to move session data across the sandbox boundary. It maintains the same directory structure (~/.claude/projects/) but inside the sandbox's isolated filesystem.

transferSession – Cross-Boundary Data Movement

The transferSession function (lines 42-70 of src/SessionStore.ts) orchestrates the actual capture process. It accepts a source store, target store, and session ID, then performs three critical operations: reading the JSON-L from the source, rewriting any cwd fields from the source's working directory to the target's working directory, and writing the modified content to the target store.

How the Orchestrator Captures Sessions

The capture process triggers automatically during agent execution when provider.captureSessions is enabled. According to the implementation in src/Orchestrator.ts (lines 71-88), the orchestrator executes the following sequence after each iteration:

  1. Creates a sandboxSessionStore using the current sandbox context and bind-mount handle
  2. Creates a hostSessionStore pointing to the host repository directory
  3. Calls await transferSession(sbStore, hStore, sessionId) to persist the sandbox session to the host

This transfer reads the session file from the sandbox using copyFileOut, streams through each JSON-L line to replace sandbox paths with host paths, and writes the result to the host filesystem using standard Node.js file operations.

Path Rewriting and Session File Locations

When transferSession processes a session file, it specifically checks each JSON-L entry for cwd fields. If an entry's current working directory matches the source store's path (the sandbox location), the function rewrites it to point to the target store's path (the host location). This ensures that absolute paths in the conversation history remain valid when the session is later loaded on the host or resumed in a new sandbox.

After a successful transfer, the captured session lives at ~/.claude/projects/<encoded-host-cwd>/<sessionId>.jsonl on the host filesystem. You can locate the exact path by calling hostStore.sessionFilePath(sessionId) on any hostSessionStore instance.

Resuming Captured Sessions

Session capture also works in reverse. When initiating a run with the resumeSession option, Sandcastle copies an existing host session back into the sandbox before starting the agent. As implemented in src/run.ts (lines 76-85), this creates both stores and executes transferSession(hStore, sbStore, resumeSession) to move the JSON-L file from the host into the sandbox's ~/.claude/projects/ directory, ensuring the agent continues with the previous conversation history intact.

Complete Implementation Example

The following TypeScript example demonstrates how to manually capture a session using the core functions:

import { hostSessionStore, sandboxSessionStore, transferSession } from "./SessionStore.js";
import type { BindMountSandboxHandle } from "./SandboxProvider.js";

async function captureSession(
  hostRepoDir: string,          // e.g. "/home/alice/project"
  sandboxDir: string,           // sandbox cwd, e.g. "/workspace"
  handle: Pick<BindMountSandboxHandle, "copyFileIn" | "copyFileOut" | "exec">,
  sessionId: string,
) {
  // Create the dual stores
  const hostStore = hostSessionStore(hostRepoDir);
  const sandboxStore = sandboxSessionStore(sandboxDir, handle, "/root/.claude/projects");

  // Transfer from sandbox to host (capture)
  await transferSession(sandboxStore, hostStore, sessionId);

  // Verify the captured file location
  console.log("Captured at:", hostStore.sessionFilePath(sessionId));
}

Running this after an agent iteration completes will leave the JSON-L file on the host filesystem, ready for inspection or subsequent runs using sandcastle run --resumeSession <id>.

Summary

  • hostSessionStore creates a filesystem-backed store at ~/.claude/projects/<encoded-cwd>/ for persisting JSON-L session files on the host machine (lines 57-76 of src/SessionStore.ts).
  • transferSession moves session data between stores while rewriting cwd paths to match the target environment (lines 42-70 of src/SessionStore.ts).
  • The Orchestrator automatically triggers capture after each iteration when captureSessions is enabled, calling transferSession with the sandbox store as source and host store as target (lines 71-88 of src/Orchestrator.ts).
  • Path encoding ensures safe filesystem operations by encoding the current working directory into the projects directory name.
  • Sessions can be resumed by reversing the transfer direction, copying from the host store back to the sandbox store before agent initialization (lines 76-85 of src/run.ts).

Frequently Asked Questions

How does hostSessionStore differ from sandboxSessionStore?

hostSessionStore writes directly to the host filesystem using standard Node.js file operations, storing files under ~/.claude/projects/<encoded-cwd>/. In contrast, sandboxSessionStore uses a bind-mount sandbox handle with copyFileIn and copyFileOut methods to access files inside the sandbox's isolated environment, requiring explicit file copy operations across the container boundary.

What happens to file paths during session transfer?

transferSession rewrites absolute paths in the JSON-L entries. Specifically, it replaces any cwd field values that match the source store's working directory with the target store's working directory. This ensures that when a session captured from a sandbox (where the path might be /workspace) is loaded on the host (where the path is /home/user/project), the working directory references remain valid.

Where are captured sessions stored on the host?

Captured sessions are stored at ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl, where <encoded-cwd> is a filesystem-safe encoding of the repository's current working directory. You can retrieve the exact path programmatically by calling the sessionFilePath(sessionId) method on a hostSessionStore instance.

Can I resume a session that was captured from a previous run?

Yes. When you provide the resumeSession option to sandcastle run, the system reverses the capture flow before starting the agent. It creates both stores and calls transferSession with the host store as the source and the sandbox store as the target, copying the JSON-L file back into the sandbox so the agent can continue the previous conversation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →