# How ModLens Recovers Pasted Images from LLM Harness Session Storage

> Learn how ModLens recovers pasted images from LLM harness session storage. It uses process inspection, harness-specific adapters, and secure file writing for efficient retrieval.

- Repository: [liustack/modlens](https://github.com/liustack/modlens)
- Tags: how-to-guide
- Published: 2026-08-25

---

**ModLens reconstructs pasted images through a three-stage pipeline: detecting the active LLM harness via process inspection, delegating extraction to harness-specific adapters, and securely writing hashed image files to private directories.**

ModLens is an open-source tool that extracts visual assets from LLM-powered development harnesses like Claude, Pi, and Opencode. When developers paste screenshots into these AI assistants, the images get embedded in proprietary session storage formats; ModLens provides a unified recovery interface to retrieve these assets regardless of the underlying harness architecture.

## The Three-Stage Recovery Pipeline

The recovery system in `liustack/modlens` abstracts harness differences into a consistent workflow implemented in `src/recoverPaste/`.

### Stage 1: Harness Detection

The [`detect.ts`](https://github.com/liustack/modlens/blob/main/detect.ts) module identifies which LLM harness is active by inspecting the runtime environment. It examines the process tree via `/proc` on Unix systems and scans for known session markers—such as Claude’s `~/.claude` directory, Pi’s `~/.cache/pi`, or Opencode’s SQLite transcript files.

The exported helper `detectHarness()` returns a `HarnessSource` object that specifies which adapter should handle the recovery. If automatic detection fails, users can bypass this stage by manually specifying a transcript directory via the `--out-dir` CLI flag.

### Stage 2: Adapter Delegation

The `adapters/` folder contains harness-specific modules that implement the `HarnessAdapter` interface defined in [`types.ts`](https://github.com/liustack/modlens/blob/main/types.ts). Each adapter provides two functions:

- `detect(source): boolean` – Confirms the adapter can handle the given source
- `recover(source, outDir): RecoveredImage[]` – Extracts and returns image metadata

**Claude Adapter** ([`adapters/claude.ts`](https://github.com/liustack/modlens/blob/main/adapters/claude.ts)): Parses Claude’s JSONL transcript files to locate image blocks, then extracts the raw base-64 payload. It also exports `claudeProjectSlug` for integration with the guard subsystem.

**Pi Adapter** ([`adapters/pi.ts`](https://github.com/liustack/modlens/blob/main/adapters/pi.ts)): Handles Pi’s custom log format using the `piExtractLine` helper to pull image bytes from temporary storage. It exports `piSessionSlug` for session identification.

**Opencode Adapter** ([`adapters/opencode.ts`](https://github.com/liustack/modlens/blob/main/adapters/opencode.ts)): Queries Opencode’s SQLite transcript database via `opencodeSourceFor()` to extract image columns from the session storage.

### Stage 3: Orchestration and Safe File Writing

The [`index.ts`](https://github.com/liustack/modlens/blob/main/index.ts) module orchestrates the recovery process. It calls `prepareOutDir()` to create a secure output directory—either a temporary folder created with `mkdtemp` using `0700` permissions, or a user-specified path verified to be a real directory owned by the current user (with symlink checks to prevent path traversal).

The system iterates over the static `ADAPTERS` array (`[claudeAdapter, piAdapter, opencodeAdapter]`), delegating recovery to the first matching adapter. For each recovered image, ModLens:
- Computes a SHA-256 hash of the byte content using `crypto.createHash('sha256')`
- Derives the file extension from the MIME type via `extensionFromMediaType()`
- Writes the file to the secure output directory

The function returns a `RecoverResult` containing the list of recovered images and their source paths.

## Harness-Specific Implementation Details

Each adapter handles unique storage mechanisms while conforming to the shared interface.

### Claude JSONL Transcript Parsing

Claude stores conversation history in JSONL format where image blocks contain base-64 encoded data. The Claude adapter in [`src/recoverPaste/adapters/claude.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/adapters/claude.ts) streams these transcript files, detects image block markers, decodes the base-64 payload, and returns the raw bytes for hashing and storage.

### Pi Custom Log Format Extraction

Pi uses a proprietary logging structure. The Pi adapter leverages `piExtractLine` from [`src/recoverPaste/adapters/pi.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/adapters/pi.ts) to parse log entries, locate references to temporarily stored image files, and read the binary data from Pi’s cache directory.

### Opencode SQLite Database Queries

Opencode persists transcripts in SQLite. The adapter in [`src/recoverPaste/adapters/opencode.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/adapters/opencode.ts) (which includes a test suite in [`adapters/opencode.test.ts`](https://github.com/liustack/modlens/blob/main/adapters/opencode.test.ts)) executes queries against the transcript database to select image columns, then buffers the results for file writing.

## Security and Extensibility Architecture

### Security Controls

ModLens implements defense-in-depth for file system operations. The `prepareOutDir()` function ensures output directories are never world-readable by enforcing `0700` permissions on temporary folders. When users specify custom output paths, the system verifies directory ownership and rejects symlinks to prevent directory traversal attacks. All output filenames are derived from content hashes (SHA-256) rather than original metadata, eliminating path injection risks.

### Extensibility Model

Adding support for new harnesses requires implementing the `HarnessAdapter` interface and registering the adapter in the `ADAPTERS` array within [`src/recoverPaste/index.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/index.ts). The modular design isolates harness-specific parsing logic while the core orchestration handles security, hashing, and file I/O consistently.

## Usage Examples

### Command-Line Recovery

Recover images from the automatically detected harness:

```bash
modlens recover-paste

```

Specify a custom output directory with ownership verification:

```bash
modlens recover-paste --out-dir ./my-images

```

### Programmatic Integration

Import the recovery function in TypeScript projects:

```ts
import { recoverPastedImages } from 'modlens/src/recoverPaste/index';

// Recover from current working directory to temporary folder
const result = await recoverPastedImages({ count: 3 });
console.log('Recovered files:', result.images.map(i => i.path));

```

### Adding a Custom Harness Adapter

Implement the interface and register the adapter:

```ts
// src/recoverPaste/adapters/myharness.ts
import type { HarnessAdapter } from '../types';

export const myHarnessAdapter: HarnessAdapter = {
  name: 'my-harness',
  detect: (source) => source.someCondition,
  recover: async (source, outDir) => {
    // Parse harness-specific storage
    // Return RecoveredImage[]
  },
};

// Register in src/recoverPaste/index.ts
import { myHarnessAdapter } from './adapters/myharness';
const ADAPTERS = [claudeAdapter, piAdapter, opencodeAdapter, myHarnessAdapter];

```

## Summary

- **Detection Strategy**: [`detect.ts`](https://github.com/liustack/modlens/blob/main/detect.ts) identifies active harnesses via process tree inspection and filesystem markers, returning a `HarnessSource` for adapter selection.
- **Adapter Pattern**: Each harness implements the `HarnessAdapter` interface with `detect()` and `recover()` methods; current adapters support Claude (JSONL), Pi (custom logs), and Opencode (SQLite).
- **Security Model**: `prepareOutDir()` enforces `0700` permissions and ownership verification; output filenames use SHA-256 content hashes to prevent collisions and injection attacks.
- **Core Orchestration**: [`src/recoverPaste/index.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/index.ts) coordinates detection, adapter iteration, and safe file writing, returning structured `RecoverResult` objects.
- **Extension Points**: New harnesses integrate by implementing the interface and adding to the `ADAPTERS` array without modifying core recovery logic.

## Frequently Asked Questions

### How does ModLens determine which LLM harness is currently running?

ModLens uses the `detectHarness()` function in [`src/recoverPaste/detect.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/detect.ts) to inspect the process tree via `/proc` on Unix systems and check for harness-specific filesystem markers like `~/.claude` for Claude or `~/.cache/pi` for Pi. This detection returns a `HarnessSource` object that determines which adapter handles the recovery.

### What security measures protect recovered image files?

The system creates output directories with `0700` permissions using `mkdtemp` for temporary folders, or validates user-specified paths for ownership and symlink integrity. All recovered files are named using SHA-256 hashes of their content via `crypto.createHash('sha256')`, preventing filename collisions and path traversal vulnerabilities.

### Can ModLens recover images from harnesses not officially supported?

Yes, the architecture supports custom adapters by implementing the `HarnessAdapter` interface defined in [`types.ts`](https://github.com/liustack/modlens/blob/main/types.ts). Developers create a module with `detect()` and `recover()` methods, then register the adapter in the `ADAPTERS` array in [`src/recoverPaste/index.ts`](https://github.com/liustack/modlens/blob/main/src/recoverPaste/index.ts) to extend support to additional LLM platforms.

### Where does ModLens store recovered images by default?

By default, ModLens creates a temporary directory using `prepareOutDir()` with restricted `0700` permissions. Users can override this behavior by specifying a custom path via the `--out-dir` CLI argument, which must be a real directory owned by the current user and cannot be a symlink.