Skip to content

Data and trust ​

Lens runs inside the notebook browser document and Python kernel. It collects selection details and notebook context so a connected agent can act on the selected result. Review the notebook and agent environment with the same care you apply to source code, data, and rendered output.

Lens() writes nothing to disk. It holds open selections, notes, and their images in kernel memory. Lens(record=True) also keeps a request record, which in the marimo editor writes files to the notebook's __marimo__/lens/ folder.

What Lens can expose ​

DataWhere it comes fromAgent-facing form
Selection note and geometryLens selection UICompact references and text
Target identity and DOM hintRendered targetCompact references and text
Captured description and rendering referenceClient metadataBounded references and text
Producing and upstream cell sourcemarimo dataflow graphBounded text
Relevant native control valuesLive marimo controlsBounded text
Annotated selection pixelsBrowser capture at selection timeLensContext.images PNG bytes
Current cell-output pixelsAgent-requested browser captureOne-use cell_image() PNG bytes
Notebook path and cell IDsActive marimo runtimeCompact references and text
Resolved requests and the code they changedresolve() with a recordcontext.history() and the file

A DOM hint can contain the element tag, role, accessible label, title, rendered text, a short structural path, and normalized bounds. A configured DOM target can therefore expose authored page text outside a notebook output. Small DOM captures can include nearby context or a client-declared containing element. Rendering references describe client project files and symbols. Lens carries those references as evidence and does not open the referenced files.

Control-value handling ​

Lens selects control state from variables referenced by relevant notebook cells. It serializes bounded scalar values and scalar sequences from supported native marimo controls.

Password controls, file payloads, composite controls, custom controls, AnyWidget state, opaque objects, and unsafe values appear as [redacted] or [unavailable]. This is structural protection for known control kinds. Lens does not inspect ordinary source strings, notes, DOM text, or rendered pixels for secrets.

Review those inputs before connecting an agent whose provider or execution environment can transmit them outside the notebook process.

In-memory lifetime ​

Open selections and selection-image bytes live in the Python Lens instance. Lens keeps them out of the notebook file and browser local storage.

  • Delete or Clear selections releases the affected selection images.
  • resolve() on a Lens() closes the resolved requests and releases their images.
  • resolve() on a Lens with a record moves the resolved requests, with each selection's annotated image unless the record sets images=False, into the request record.
  • lens.close() and notebook-runtime teardown release Lens-owned selection state and images.

LensContext is a detached copy. Code that receives a context can retain its references, rendered text, or copied image bytes after the Lens instance changes or closes.

Request record ​

A Lens keeps a record when you construct it with record, as in Keep a record. The automatic Lens that marimo shows keeps none. Record lists every option.

In the marimo editor, a record with the default keep="notebook" is a JSON Lines file next to marimo's session files. Each resolve() appends one line per resolved request to it. Each resolved selection's annotated image goes beside it, named by its SHA-256 digest:

text
__marimo__/lens/analysis.py.jsonl
__marimo__/lens/images/3f1c…e9.png

marimo places __marimo__ beside the notebook, or under sys.pycache_prefix when Python sets one. A line holds:

  • The request: its ID, channel, label, note, and the agent's summary.
  • When it opened and when it was resolved.
  • What it pointed at: the target, anchor, and captured description of a selection, and the producing cells.
  • The selection's image, as trace.image with its digest, size, capture time, and whether the selection moved after the image was taken. A record built with images=False leaves it out.
  • Each cell that changed while it was open, with the code before and after.
  • The other requests that were open at the same time.

The record keeps notes, code, and the images the analyst marked. It holds no control values or cell outputs. Lens writes an image whole under its digest before the line that names it, so a line never points at a half-written image. Lens appends under a file lock, so kernels that serve the same notebook write whole lines. A file system that offers no lock fails the write.

A line that Lens cannot write, for example because the folder is read-only, stays in memory, and Lens writes it at the next resolution or context() call. An unsaved notebook keeps its lines in memory until it has a file name. Lens keeps up to 256 such lines. Past that, resolve() raises record_full and leaves the requests open, so save the notebook or make its folder writable. Lines still in memory end with the kernel, so save the notebook before you restart it.

When marimo renames or moves the notebook, Lens moves the record to the new name at its next read or write and copies the images its lines name into the new folder. Rename the record file too when you rename the notebook outside marimo.

A keep="session" record, a marimo app (marimo run), and marimo's browser runtime, which runs Python through Pyodide, keep the record in memory until the runtime restarts and write no file. A memory record holds 1,024 lines within 64 MiB and drops the oldest resolutions first. An app visitor's History shows only the requests resolved in that visitor's session.

The Lenses of one kernel share one record per storage. A chat request belongs to no Lens, so the record of the Lens that resolves it keeps it.

Lens never edits .gitignore. Committing __marimo__/lens/ publishes every recorded note, the code each request changed, and every image the analyst marked, so review the folder before you share it. Delete the folder to clear the record, its images, and History. Lens starts a new record with the next resolution. Remove record from the Lens to stop recording.

Query the record from a notebook with DuckDB, which marimo SQL cells use:

python
_requests = mo.sql(
    f"""
    SELECT resolvedAt, channel, label, note, len(changes) AS changed_cells,
        'images/' || trace.image.sha256 || '.png' AS image
    FROM read_json_auto('__marimo__/lens/analysis.py.jsonl')
    ORDER BY resolvedAt
    """
)

Agent transfer boundary ​

Lens exposes context inside the live kernel. The connected agent integration decides how to read, copy, prompt with, or transmit that context. Lens does not control the agent provider's retention or network policy.

Some image readers require a file path. The packaged Agent Skill writes PNG bytes to a private temporary file for that read and requires the agent to delete the file after its final use. The notebook kernel and image reader must share a filesystem for this path-based transfer.

Browser document boundary ​

Lens can inspect and capture content in its owner document, open shadow roots, and accessible same-origin iframe documents. Cross-origin frames are isolated by the browser. External images, styles, fonts, canvas content, and inaccessible frames can make capture fail or produce a bounded error.

Image failure does not discard the selection. Target identity, geometry, note, DOM hint, producing-cell references, and available graph context remain independent inputs.

Read How Lens works for the context forms and image states, and LensContext for their exact shapes. Read Custom targets before widening the dom_selector policy. Report security vulnerabilities through the security policy.