Python API
Use marimo_lens.agent.connect() in the live notebook kernel to reuse an existing Lens. Notebook authors can mount Lens when the host has not already provided one.
| Task | API |
|---|---|
| Discover existing instances | discover() |
| Add the notebook interface | Lens |
| Read selections, source, and images | Lens.context(), LensContext |
| Show agent work and return a result | start_activity(), stop_activity(), reveal() |
| Move addressed requests to History | resolve() |
| Show a user-paced notebook walkthrough | reveal() |
| Connect an agent or capture current output | discover(), connect(), MountedLens |
| Release a Lens instance | close() |
| Share the notebook's Lens with a host | automatic_lens(), notebook_lens() |
The caller roles are:
- Notebook authors construct
Lensand keep it mounted with the notebook. - Code-mode agents call
marimo_lens.agent.connect()and work through aMountedLenshandle. - marimo and host integrations call the host API to show one Lens per notebook runtime.
Authors and agents share context, activity, reveal, and resolution behavior. The agent handle adds stable reconnection identity and current cell-output capture.
Source version
This reference follows the repository's main branch. PyPI follows tagged releases. Read Compatibility when the installed signatures differ.
Notebook-author API
Install marimo-lens in the notebook environment. The examples use one mounted lens instance, created in the Lens example. For installation and a first selection, see Getting started.
The top-level package exports:
from marimo_lens import (
ActivityHandle,
CellReference,
Lens,
LensContext,
LensError,
LensReferences,
NotebookReference,
SelectionReference,
SelectionTargetReference,
__version__,
)__version__ comes from the installed marimo-lens distribution metadata.
Lens
Lens(*, dom_selector: str | None = None) -> LensCreates the Python widget and browser UI that own one Lens instance.
dom_selector: str | Noneadds configured DOM targets to the default notebook targets and regions declared with Lens target or source attributes. See Custom targets. The string is stripped and accepts at most 1,024 UTF-16 code units.- Returns a renderable
Lensinstance. - Raises
TypeErrorfor a non-string selector. - Raises
ValueErrorfor an empty or oversized selector. - The browser reports invalid CSS syntax when the view mounts.
from marimo_lens import Lens
lens = Lens()
lensKeep the value mounted while people create selections and agents call its methods. Read Custom targets before configuring host DOM targets.
Lens.context
lens.context() -> LensContext
Returns detached selection references, lazy graph-context text, available selection-image bytes, and the captured selection-state revision.
context = lens.context()
selection = context.currentThe object does not update. Call context() again after notebook or Lens state changes. Existing contexts remain readable after lens.close().
Raises LensError(code="lens_closed") after the Lens closes.
Read the LensContext reference for every field and degradation rule.
Lens.start_activity
lens.start_activity(
target: str | SelectionReference,
*,
expected_revision: int | None = None,
duration_ms: int | None = None,
label: str | None = None,
message: str | None = None,
) -> ActivityHandleMarks one selection or cell as the current work location and returns the opaque owner accepted by stop_activity().
context = lens.context()
selection = context.current
if selection is not None:
activity = lens.start_activity(
selection,
expected_revision=context.revision,
label="Updating selected result",
message="Inspecting the target and its producing cells.",
)Pass a SelectionReference and its captured expected_revision to address the selected target. Pass a cell ID string for a notebook walkthrough. A cell address can omit expected_revision and must identify a current graph member.
duration_ms=None keeps activity visible until a matching stop, later attention, or teardown. A duration from 1 through 300,000 milliseconds clears it after that hold. label defaults to Working in the browser. Labels accept 40 UTF-16 code units and activity messages accept 240. Selection state does not change.
Raises LensError for a closed Lens, stale selection revision, missing selection, unavailable runtime, or missing cell. Invalid inputs raise TypeError or ValueError.
Lens.stop_activity
lens.stop_activity(activity) -> None
Stops activity when activity still owns the current presentation.
lens.stop_activity(activity)ActivityHandle is a string-backed, JSON-safe opaque value. A stale handle has no effect. A non-string value raises TypeError. An empty or oversized value raises ValueError. A closed Lens raises LensError(code="lens_closed").
Lens.reveal
lens.reveal(
target: str | SelectionReference | Sequence[RevealStep],
*,
expected_revision: int | None = None,
duration_ms: int | None,
label: str | None = None,
message: str | None = None,
) -> NoneBrings a cell, selection, or ordered sequence of steps into view. A single target is a one-step reveal. Multiple steps add previous/next controls in the popover header and scroll smoothly, respecting reduced motion.
context = lens.context()
selection = context.current
if selection is not None:
lens.reveal(
selection,
expected_revision=context.revision,
duration_ms=8_000,
label="Updated result",
message="Verified the change and brought the selected target into view.",
)For a user-paced walkthrough, pass 1–16 RevealStep dictionaries:
lens.reveal(
[
{
"target": "BYtC",
"label": "The question",
"message": "Compare demand with capacity.",
},
{"target": "rAqT", "label": "The evidence"},
{"target": "mNwP", "label": "The next step"},
],
duration_ms=None,
)Each step requires target (a cell ID or SelectionReference) and accepts optional label and message. Put explanations on the steps, rather than in top-level label or message arguments. Selection references in the sequence share expected_revision from the same captured context. The whole sequence is validated before publication.
Pass duration_ms=None to hold until dismissal or replacement. A finite duration limits the whole reveal, including time spent on previous steps. Navigation never resets the timer or advances automatically. A held single reveal has a dismiss button; multiple steps also have arrows and a count. Reveals are transient and do not enter notebook state or History. In a live notebook, rerunning or deleting a referenced cell ends the reveal.
Target and revision rules match start_activity(). Reveal preserves selection state and keyboard focus, and replaces current activity. Call reveal() before resolve() with the same captured revision in one kernel call. History retains the target for the reveal, and the receipt appears after the reveal ends. A later attention event replaces the reveal.
duration_ms accepts 1 through 300,000 milliseconds. Labels accept 40 UTF-16 code units. Reveal messages accept 1,000. See Errors and limits for the complete validation contract.
Lens.resolve
lens.resolve(
selection_ids: str | Sequence[str],
*,
expected_revision: int,
summary: str | None = None,
) -> intMoves one or more Open selections into History and returns the next selection-state revision.
context = lens.context()
selection_ids = [item["id"] for item in context.references["selections"]]
if selection_ids:
revision = lens.resolve(
selection_ids,
expected_revision=context.revision,
summary="Updated the result and verified the affected cells.",
)Pass one ID string or a sequence of up to 64 unique strings. Lens validates the entire batch before changing state. The resolved selections share one resulting revision and optional summary of up to 240 UTF-16 code units. Their selection-image bytes are released.
Include summary to tell the user what changed or what you found and how you verified it. Lens stores it beside the original request in each History entry and displays it in the resolution receipt. Resolve separately with distinct summaries when requests have different outcomes, passing the returned revision to the next call.
The state transition commits before Lens sends its best-effort resolution receipt. A receipt delivery failure does not roll back History. Expected errors are lens_closed, revision_conflict, selection_not_found, and selection_context_limit.
Lens.close
lens.close() -> None
Closes the Lens instance, cancels pending cell-output capture, and releases Open selections, History entries, and Lens-owned selection images. Repeated calls have no effect.
Later public operations raise LensError(code="lens_closed").
Host API
marimo and host integrations call these functions in the live notebook kernel. Together they keep one Lens per notebook runtime.
automatic_lens
automatic_lens() -> Lens | None
Returns a new Lens for marimo to show, or None when the notebook needs none. marimo calls it after a notebook cell that imports marimo runs successfully in the editor, then displays the returned Lens. The Lens belongs to that cell.
Returns None when:
- The kernel serves an app (
marimo run), a script, or a test. - The caller runs outside a notebook cell, such as a code-mode scratchpad call.
- The runtime already holds an open Lens.
- A notebook cell constructs its own Lens, as in
Lens(...)afterfrom marimo_lens import Lens, ormarimo_lens.Lens(...). The import and the call can sit in different cells. An import used only for type checks keeps the automatic Lens.
A Lens that a notebook cell creates later, such as a newly added Lens(dom_selector=...) cell, closes the automatic Lens and its Open selections. Rerunning or deleting the cell that owns the automatic Lens also closes it. The next successful run of a cell that imports marimo shows a new one when the notebook has no other Lens.
notebook_lens
notebook_lens() -> Lens | None
Returns the oldest open Lens in the active marimo runtime, or None. A host renders it in another browser document, such as a preview, to share the notebook's Lens:
import marimo_lens
lens = marimo_lens.notebook_lens()Each browser document gives one Lens view interaction ownership, so the notebook and the host document each show a dock for the same selections.
Agent adapter
import marimo_lens loads marimo_lens.agent, the module marimo registers as the lens capability. Read the agent resources from the installed package:
import marimo_lens
plugin = marimo_lens.agent.plugin()
skill = marimo_lens.agent.skill()plugin
plugin() -> agent_plugins.Plugin
Returns the Agent Plugin resource bundle installed with the current marimo-lens distribution.
Raises agent_plugins.AgentPluginError when distribution metadata or the packaged plugin is unavailable. Reinstall the same marimo-lens version before retrying.
skill
skill() -> agent_plugins.Skill
Returns the packaged marimo-lens Agent Skill.
skill.file("SKILL.md")gives the checked instruction path.skill.bodygives the Markdown instruction body.skill.filesgives the packaged resource inventory.
help(marimo_lens.agent) includes this core skill through agent_plugins.read("marimo-lens"). Load linked workflows with skill.file("references/selections.md").read_text(encoding="utf-8") when needed.
Raises agent_plugins.AgentPluginError when the plugin contains no Lens skill.
add_lens_cell
add_lens_cell(ctx) -> str
Returns the existing agent-managed Lens cell ID or queues one collapsed Lens cell and returns its new ID.
import marimo._code_mode as cm
import marimo_lens
async with cm.get_context() as context:
cell_id = marimo_lens.agent.add_lens_cell(context)Call connect() or discover() first to reuse an authored or automatically mounted Lens. This helper searches for agent-managed cells.
The code-mode context creates and runs a queued cell when its async context manager exits. Connect in a later kernel call after the browser renders Lens.
An existing agent-managed cell is queued to run again unless it still holds an open Lens or its status is queued, running, or disabled. This mounts a new Lens in a notebook reopened in a new kernel, after a failed first run, or after close(). A cell whose Lens is open keeps that Lens and its Open selections.
ctx must expose create_cell(), run_cell(), and cells.find(), and found cells must report status. Objects without those methods raise TypeError. Several agent-managed Lens cells raise LensError(code="lens_ambiguous").
discover
discover(context=None) -> tuple[MountedLens, ...]
Returns the available Lens handles without creating a widget or notebook cell. An empty tuple means no instance is discoverable in this call. Multiple results let an agent inspect their contexts and choose an identity for connect().
import marimo._code_mode as cm
import marimo_lens
available = marimo_lens.agent.discover(cm.get_context())
for candidate in available:
print(candidate.identity, candidate.context().current)Discovery combines browser-ready instances in the current runtime with Lens objects and supported marimo wrappers in context.globals. Aliases of one object produce one handle. Automatically mounted instances need no global variable, but must finish browser registration before this path finds them. Globals can expose an instance before it is rendered. Closed objects are excluded.
Order implies no preference or browser ownership. Handles retain the same identity across calls while their Lens exists. Discovery does not keep an unreferenced instance alive after its returned handles are released. A supplied context without a globals mapping raises TypeError.
connect
connect(context=None, *, identity=None) -> MountedLens
Selects one of the available handles from discover(). Reuses authored and automatically mounted instances without creating another Lens.
import marimo._code_mode as cm
import marimo_lens
mounted = marimo_lens.agent.connect(cm.get_context())context adds Lens objects found in code-mode globals to browser-ready Lens registrations. When exactly one browser-ready Lens owns a displayed view, connect() selects it even if context.globals contains additional Lens objects. With no browser-ready Lens, one available context object is selected. Use identity to reconnect to the same Lens in a later kernel call or to choose a specific candidate when several are available.
Raises LensError(code="lens_unavailable") when no matching Lens exists. Raises LensError(code="lens_ambiguous") when several browser-ready instances exist, or when several context objects exist without a browser-ready instance, and no identity selects one. A context without a globals mapping or a non-string identity raises TypeError. An empty identity raises ValueError.
MountedLens
MountedLens remains attached to one live Lens instance. It can address any selection owned by that instance.
| Member | Contract |
|---|---|
identity | Opaque string for reconnecting across kernel calls. |
context() | Delegates to Lens.context(). |
cell_image(cell_id, *, expected_revision) | Requests a fresh, unmarked cell-output PNG. |
start_activity(...) | Delegates to Lens.start_activity(). |
stop_activity(activity) | Delegates to Lens.stop_activity(). |
reveal(...) | Delegates to Lens.reveal(). |
resolve(...) | Delegates to Lens.resolve(). |
mounted.cell_image(cell_id, *, expected_revision) -> bytes | None
The first call starts browser capture and returns None. End that kernel call, reconnect to the same Lens, and repeat the same cell ID and revision. The completed call returns and consumes the PNG bytes.
context = mounted.context()
selection = context.current
if selection is not None and selection["cells"]:
cell_id = selection["cells"][0]["id"]
png = mounted.cell_image(
cell_id,
expected_revision=context.revision,
)One capture can be pending per Lens. Finish it before requesting another cell. A stale revision raises revision_conflict. A different cell while capture is pending raises capture_busy. A terminal result consumes the capture slot, so another call starts a new capture. Browser capture has a 15-second deadline and the Python request expires after 20 seconds. The operation requires a current graph member and a browser-ready Lens view. Read How Lens works for the difference between a selection image and a cell-output image.
LensError
LensError extends RuntimeError and exposes:
code: str, a stable machine-readable failure code.revision: int | None, the current selection-state revision when available.
str(error) starts with the code, as in lens_unavailable: No Lens is available in the active notebook., so a traceback reported by a code-mode integration names the recovery code.
Read Errors and limits for every code, recovery action, argument rule, and bound. Read Connect an agent for the complete workflow.