How Session Capture Works in Sandcastle Using `sandboxSessionStore`

Sandcastle captures AI agent sessions by using sandboxSessionStore to extract JSONL session files from a running container, transferSession to rewrite path references from sandbox to host paths, and hostSessionStore to persist the final file on your local filesystem for later analysis.

Session capture in Sandcastle enables you to record every interaction between the Claude Code agent and your codebase inside a sandboxed environment. This feature leverages the sandboxSessionStore abstraction to move session files from ephemeral containers to persistent host storage, making debugging and audit logging possible. According to the mattpocock/sandcastle source code, the implementation relies on a bind-mount handle and a path-rewriting transfer mechanism to ensure session integrity across filesystem boundaries.

The Session Capture Architecture

Sandcastle’s session capture system comprises three coordinated components defined in src/SessionStore.ts.

Host Session Store (hostSessionStore)

The host session store manages JSONL files on your local filesystem. Located at lines 57-75 in src/SessionStore.ts, this implementation creates the necessary directory structure under ~/.claude/projects/<encoded-cwd>/ and provides methods to read and write session files. It returns absolute paths via sessionFilePath(id) and handles the final persistence of captured sessions.

Sandbox Session Store (sandboxSessionStore)

The sandbox session store acts as a bridge to the containerized environment. Implemented at lines 91-135 in src/SessionStore.ts, this store accepts a bindMountHandle (providing copyFileIn, copyFileOut, and exec capabilities) to manipulate files inside the sandbox. It uses POSIX path joins for container-side paths and translates high-level storage operations into bind-mount file transfers. This component is essential because it allows Sandcastle to access session files while the sandbox process is still alive.

Session Transfer Helper (transferSession)

The transferSession function handles the actual migration of session data between stores. Found at lines 142-170 in src/SessionStore.ts, this utility reads the entire JSONL from the source store, rewrites every "cwd" field from the sandbox working directory to the host working directory, and writes the transformed payload to the destination. This path rewriting ensures that session logs remain valid references when moved from container to host.

How the Orchestrator Triggers Session Capture

The capture logic executes within src/Orchestrator.ts (lines 68-99) after the agent finishes running. The orchestrator determines whether to capture based on the provider.captureSessions flag defined in src/AgentProvider.ts (lines 115-120).

When captureSessions evaluates to true alongside a valid sessionId and bindMountHandle, Sandcastle executes the following sequence:

  1. Initialize stores: Creates a sandbox-side store using sandboxSessionStore(ctx.sandboxRepoDir, bindMountHandle, sandboxProjectsDir) and a host-side store using hostSessionStore(hostRepoDir, hostProjectsDir).

  2. Transfer data: Invokes transferSession(sbStore, hStore, sessionId) to move the JSONL file while rewriting path references.

  3. Record path: Stores hStore.sessionFilePath(sessionId) in the OrchestratorResult as sessionFilePath.

  4. Parse usage: Optionally calls provider.parseSessionUsage if implemented, reading the captured file via hStore.readSession(sessionId) to extract token statistics.

This process occurs while the sandbox process remains alive, guaranteeing access to temporary container files through the bind-mount handle.

Implementing Session Capture in Your Code

You can interact with session capture programmatically using the store factory functions and transfer utilities.

Manual Session Capture

To manually transfer a session between sandbox and host:

import {
  hostSessionStore,
  sandboxSessionStore,
  transferSession,
} from "sandcastle/src/SessionStore";
import type { BindMountSandboxHandle } from "sandcastle/src/SandboxProvider";

async function captureSession(
  sessionId: string,
  hostCwd: string,
  sandboxCwd: string,
  bindMount: BindMountSandboxHandle,
) {
  const hostStore = hostSessionStore(hostCwd);
  const sandboxStore = sandboxSessionStore(
    sandboxCwd,
    bindMount,
    "/root/.claude/projects",
  );

  await transferSession(sandboxStore, hostStore, sessionId);
  return hostStore.sessionFilePath(sessionId);
}

Configuring Provider Settings

Claude Code providers enable session capture by default via the captureSessions flag:

import { claudeCode } from "sandcastle/src/AgentProvider";

// Enable capture (default)
const provider = claudeCode("claude-sonnet-4-6", {
  captureSessions: true,
});

// Disable capture for faster runs
const fastProvider = claudeCode("claude-sonnet-4-6", {
  captureSessions: false,
});

Reading Captured Sessions

Access captured session data after transfer:

import { hostSessionStore } from "sandcastle/src/SessionStore";

async function analyzeSession(hostCwd: string, sessionId: string) {
  const store = hostSessionStore(hostCwd);
  const jsonlContent = await store.readSession(sessionId);
  console.log("Session interactions:", jsonlContent);
}

Summary

  • sandboxSessionStore provides container-side file access via bind-mount handles, enabling session extraction from running sandboxes.
  • transferSession rewrites cwd paths during the copy operation to ensure session files reference host paths rather than ephemeral container directories.
  • hostSessionStore persists captured sessions to ~/.claude/projects/<encoded-cwd>/ on the local filesystem.
  • The orchestrator coordinates the capture automatically when provider.captureSessions is true, storing the final path in OrchestratorResult.
  • Session capture requires a bind-mount handle and occurs while the sandbox process remains active to ensure file accessibility.

Frequently Asked Questions

How does sandboxSessionStore access files inside a running container?

The sandboxSessionStore function receives a BindMountSandboxHandle parameter containing methods like copyFileIn, copyFileOut, and exec. As implemented in src/SessionStore.ts (lines 91-135), these methods interact with the container's filesystem through bind mounts, allowing the store to read and write files without requiring SSH or network protocols. This design ensures session files remain accessible while the sandbox process is alive.

What path transformations occur during session transfer?

According to src/SessionStore.ts (lines 142-170), the transferSession function performs a search-and-replace operation on every line of the JSONL session file. It identifies "cwd" fields containing the sandbox working directory and rewrites them to match the host working directory. This transformation ensures that file references in the captured session remain valid when analyzed on the host system outside the container.

Can I use session capture with non-Claude Code agents?

Session capture is supported for any provider that sets captureSessions to true in src/AgentProvider.ts (lines 115-120). However, Claude Code defaults to enabling this feature while other providers may disable it if they do not generate compatible session files. You must also ensure the provider returns a valid sessionId and that you are using a bind-mount sandbox handle rather than an SSH-based one.

Where are captured sessions stored on the host filesystem?

Captured sessions are written to ~/.claude/projects/<encoded-cwd>/ as JSONL files. The hostSessionStore implementation in src/SessionStore.ts (lines 57-75) handles directory creation and file management, returning absolute paths via sessionFilePath(id). The orchestrator stores this path in the sessionFilePath property of the OrchestratorResult object returned after agent execution.

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 →