Skip to content

Minimal CPython REPL runtime (rlm.repl) - #1685

Open
snimu wants to merge 6 commits into
repl/01-bash-toolfrom
repl/02-runtime
Open

Minimal CPython REPL runtime (rlm.repl)#1685
snimu wants to merge 6 commits into
repl/01-bash-toolfrom
repl/02-runtime

Conversation

@snimu

@snimu snimu commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Adds rlm.repl: a minimal CPython REPL runtime (one 635-line file + a 127-line protocol doc) that speaks newline-delimited JSON over stdio — execute / interrupt / snapshot / restore / shutdown in, streamed stdout / stderr / result / display / error / done events out.

Design points:

  • Top-level await (PyCF_ALLOW_TOP_LEVEL_AWAIT) on one persistent event loop; background tasks keep running between cells; last-expression repr echo like a REPL.
  • Clean CPython tracebacks: cell sources registered in linecache (so tracebacks show the offending line), runtime-internal frames stripped, no ANSI decoration.
  • The protocol channel lives on a private dup of fd 1 while user fds are piped through pump threads — even C extensions writing to fd 1 cannot corrupt framing.
  • Interrupts work for both sync-blocking and await-suspended cells (SIGINT dispatch on the main thread with task-cancel fallback); interrupts for not-yet-started requests are parked and delivered at activation.
  • First-class snapshot/restore with dill, matching the existing snapshot semantics; restoring old IPython-era snapshots skips In/Out/get_ipython.
  • bash() from the previous PR works unchanged on the persistent loop.

Standalone in this PR: the runtime is fully tested (25 new tests incl. both interrupt paths, direct-fd capture, framing under noise) but the host still runs ipykernel — the host swap is the next PR. Measured: ~30 ms start-to-ready (ipykernel: ~1.2 s), ~0.13 ms per-cell round-trip.

Ticket: Linear ticket to be linked shortly (Linear API auth is being restored).

Stack

This is PR 2 of 4; each PR merges into its predecessor (merge bottom-up):

  1. Async bash() tool for the kernel runtime #1684 — Async bash() tool for the kernel runtime
  2. Minimal CPython REPL runtime (rlm.repl) #1685 — Minimal CPython REPL runtime (rlm.repl)
  3. Run the kernel on the REPL runtime #1686 — Run the kernel on the REPL runtime
  4. Complete the REPL cutover: remove IPython, ZMQ, and cell magics #1687 — Complete the REPL cutover: remove IPython, ZMQ, and cell magics

Note

Medium Risk
New arbitrary-code execution runtime with SIGINT dispatch, fd remapping, and dill pickle restore. Not yet the production kernel path, but bugs here will become session-critical once the host swaps over.

Overview
Adds a standalone python -m rlm.repl runtime that speaks newline-delimited JSON over stdio, as a future replacement for ipykernel (host still uses Jupyter in this PR).

Cells run in one persistent __main__ namespace on a single asyncio loop with top-level await, trailing-expression repr, captured stdout/stderr (including direct fd writes), emit() display events, and KeyboardInterrupt for both sync-blocked and await-suspended work. Snapshot/restore pickles the user namespace with dill (lazy import; size caps and optional prune). Shutdown kills live bash() children.

Also delays ipykernel/IPython imports so the package can load outside a Jupyter kernel, documents the protocol in repl.md, and covers the runtime with subprocess tests.

Reviewed by Cursor Bugbot for commit e017cad. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add rlm.repl CPython REPL runtime with JSON-lines protocol

  • New rlm.repl module implements a subprocess-based REPL runtime speaking a newline-delimited JSON protocol over stdin/stdout, supporting cell execution with trailing-expression result echo, top-level await, and output capture via pipe pumps.
  • Interrupt handling covers sync-blocked, await-suspended, and background-task-hogged scenarios via a SIGINT handler that raises KeyboardInterrupt into the active task or cancels and re-targets as needed.
  • Snapshot/restore support uses dill to serialize namespace variables with per-variable and aggregate byte caps, manifest generation, and prune-oversized reporting.
  • Refactors rlm.__init__.py to defer ipykernel.comm.Comm and IPython.get_ipython imports to call time, so importing rlm no longer fails in environments without ipykernel.
  • Adds comprehensive protocol docs in repl.md and a full test suite in test_repl.py.
  • Risk: rlm.host_request now raises RuntimeError at call time (not import time) when ipykernel.comm.Comm is unavailable; callers that previously relied on import-time failure will see the error later.

Macroscope summarized e017cad.

Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py Outdated
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py Outdated
Comment thread prime-agent-runtime/src/rlm/repl.md Outdated
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py
Comment thread prime-agent-runtime/src/rlm/repl.py Outdated
Comment thread prime-agent-runtime/src/rlm/repl.py
- revalidate targeted interrupts through SIGINT delivery so a stale signal
  cannot cancel a later request
- preserve a parked untargeted interrupt while another request is inflight
- reject non-string interrupt ids and validate snapshot options
  (prune_oversized bool, max_bytes/max_variable_bytes present => int)
- guard str(exc) in error/skip/fail reasons so a broken __str__ cannot
  kill the runtime
- fail the snapshot when the manifest write fails (before any pruning)
- report 'snapshot not found' reason on restore of a missing path
- wait unbounded for the output-pump drain marker so done never precedes
  captured output and marker bytes never leak
- document that aggregate-cap skips are not pruned
Comment thread prime-agent-runtime/src/rlm/repl.py

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e017cad. Configure here.

Comment thread prime-agent-runtime/src/rlm/repl.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant