Run code in a persistent kernel using a list of cells.

Each call submits one or more cells. Cells run in array order. State persists within each language across cells, tool calls, and subagents spawned with `task`; variables a parent or subagent declares are visible to the other on the same shared executor.

Cell fields:

  • language — {{#if py}}"py" for the IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}"js" for the persistent JavaScript VM{{/if}}.
  • code — cell body, verbatim. Newlines, quotes, and indentation are JSON-encoded; no fences, no headers.
  • title (optional) — short label shown in the transcript (e.g. "imports", "load config").
  • timeout (optional) — per-cell inactivity budget in seconds (1-600). Default 30. The cell is interrupted only after this long with no progress, and every status event (agent() updates, log()/phase(), tool activity) resets the clock — so a long agent()/parallel() fanout that keeps reporting progress is not killed. Raw print/stdout does not reset it; raise timeout for a cell that runs long without emitting status.
  • reset (optional) — wipe this cell's language kernel before running.{{#ifAll py js}} Reset is per-language: a py cell's reset does not touch the JavaScript VM and vice versa.{{/ifAll}}

Work incrementally:

  • One logical step per cell (imports, define, test, use).
  • Pass multiple small cells in one call.
  • Define small reusable functions for individual debugging.
  • Put workflow explanations in the assistant message or title — never inside cell code. {{#if py}}- Python cells run inside an IPython kernel with a live event loop. Use top-level await directly (e.g. await main()); asyncio.run(…) raises "cannot be called from a running event loop".{{/if}} On failure: errors identify the failing cell (e.g., "Cell 3 failed"). Resubmit only the fixed cell (or fixed cell + remaining cells).
{{#ifAll py js}}Same helpers in both runtimes with the same positional argument order. Python: trailing options as keyword args. JavaScript: trailing options as a trailing object literal. JavaScript helpers are async and `await`able; Python helpers run synchronously.{{else}}{{#if py}}Helpers run synchronously. Trailing options are keyword arguments.{{/if}}{{#if js}}Helpers are async and `await`able. Trailing options are a final object literal.{{/if}}{{/ifAll}} ``` display(value) → None Render a value in the current cell output. print(value, ...) → None Print to the cell's text output. read(path, offset?=1, limit?=None) → str Read file contents as text. offset/limit are 1-indexed line bounds. write(path, content) → str Write content to a file (creates parent directories). Returns the resolved path. append(path, content) → str Append content to a file. Returns the resolved path. tree(path?=".", max_depth?=3, show_hidden?=False) → str Render a directory tree. diff(a, b) → str Unified diff between two files. env(key?=None, value?=None) → str | None | dict No args → full environment as dict. One arg → value of `key`. Two args → set `key=value` and return value. output(*ids, format?="raw", query?=None, offset?=None, limit?=None) → str | dict | list[dict] Read task/agent output by ID. Single id returns text/dict; multiple ids return a list. tool.(args) → unknown Invoke any session tool by name. `args` is the tool's parameter object. llm(prompt, model?="default", system?=None, schema?=None) → str | dict Oneshot, stateless LLM call (no history, no tools). `model` picks a tier: "smol" (fast), "default" (this session's model), "slow" (most capable). Pass `system` for a system prompt. Pass a JSON-Schema `schema` to force structured output and get the parsed object back; otherwise returns the completion text. agent(prompt, agent_type?="task", model?=None, context?=None, label?=None, schema?=None) → str | dict Run a subagent and return its final output. Defaults to the bundled "task" agent; pass `agent_type`/`agentType` for another discovered agent. Pass a JSON-Schema `schema` to force structured output and get the parsed object back. parallel(thunks, concurrency?=4) → list Run thunks (callables) through a bounded pool (default 4, max 16), preserving input order. Barrier: returns once all finish; a thunk that throws propagates. pipeline(items, ...stages, concurrency?=4) → list Map each item through stages left-to-right; a barrier runs between stages (every item clears stage N before stage N+1). Each stage is a one-arg callable: stage 1 gets the original item, later stages get the previous result. log(message) → None Emit a progress line above the status tree. phase(title) → None Start a phase; the status lines that follow group under it. budget → per-turn token budget {{#if py}}`budget.total` (ceiling or None), `budget.spent()` (output tokens this turn), `budget.remaining()` (math.inf when no ceiling), `budget.hard` (bool).{{/if}}{{#if js}}`await budget.total()` (ceiling or null), `await budget.spent()`, `await budget.remaining()` (Infinity when no ceiling), `await budget.hard()`.{{/if}} A ceiling is set by a `+Nk` message directive (advisory) or `+Nk!`/Goal Mode (hard — `agent()` refuses to spawn past it); otherwise total is None/null and spend is still tracked across the turn (main loop + eval subagents). ``` Cells render like a Jupyter notebook. `display(value)` renders non-presentable data as an interactive JSON tree. Presentable values (figures, images, dataframes, etc.) use their native representation. {{#if js}}- **js**: the VM exposes a selective `process` subset, Web APIs, `Buffer`, `fs/promises`, and the `Bun` global. {{/if}} {{#if py}}```json { "cells": [ { "language": "py", "title": "imports", "timeout": 10, "code": "import json\nfrom pathlib import Path" }, { "language": "py", "title": "load config", "code": "data = json.loads(read('package.json'))\ndisplay(data)" } ] } ```{{/if}}{{#ifAll py js}}

{{/ifAll}}{{#if js}}```json { "cells": [ { "language": "js", "title": "summary", "reset": true, "code": "const data = JSON.parse(await read('package.json'));\ndisplay(data);\nreturn data.name;" } ] }

</example>