npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@florianguyonnet/pi-rlm

v0.2.0

Published

Recursive Language Models for pi: a persistent Python REPL with rlm() sub-agent recursion, as a single extension

Readme

pi-rlm

Recursive Language Models for pi — as a single extension, no fork required.

The agent gets one tool, ipython: a persistent Python REPL whose namespace doubles as long-term memory. Inside that namespace lives rlm(prompt, context=None), which spawns a recursive sub-agent (a fresh pi process with its own clean context window) and returns its answer as a plain Python string. Because sub-results land in REPL variables instead of the conversation, the agent can fan out over far more text than fits in its own context window.

This is a port of the RLM capabilities of prime-agent (which vendors a whole pi fork to get them) onto pi's public extension API — including the namespace snapshot and the continual harness (/refine).

pi  ── ipython tool ──► KernelProcess (src/kernel.ts)
                            │  unix socket, JSONL
                            ▼
                     rlm_kernel.py  ← persistent namespace (stdlib only)
                            │  rlm("summarize", context=chunk)
                            ▼
                     child pi -e pi-rlm --rlm   (src/child.ts)
                            │  PI_RLM_DEPTH + 1, may recurse further
                            ▼
                     answer returns as a Python string

Install

pi install npm:@florianguyonnet/pi-rlm            # from npm (scoped: the bare `pi-rlm` name is taken)
pi install git:github.com/florianguyonnet/pi-rlm  # from git
pi install /path/to/pi-rlm                        # local checkout
pi -e /path/to/pi-rlm/src/index.ts                # one-shot, no install

Requires Python 3 on PATH (stdlib only — no venv, no pip install). Override with PI_RLM_PYTHON.

Usage

RLM mode — the pure REPL agent. Active tools collapse to ipython only and the system prompt is replaced with the RLM prompt:

pi --rlm "Summarize each chapter of @huge.log and reconcile the totals"

or toggle it mid-session with /rlm. Toggle off to restore the full toolset (ipython stays available).

Light mode — with the extension installed but RLM mode off, ipython + rlm() sit alongside pi's normal tools, and a short addendum documents them.

Inside the kernel:

text = open("huge.log").read()          # big data stays in the REPL
chunks = [text[i:i+50_000] for i in range(0, len(text), 50_000)]
notes = [rlm("Summarize this chunk, keep all numbers", context=c) for c in chunks]
len(notes), sum(len(n) for n in notes)  # repr of trailing expr is returned

user_prompt (the current user request) is injected into the namespace before each turn.

Namespace snapshots

When the session ends, picklable data variables are pickled to ~/.pi/agent/pi-rlm/snapshots/<session-id>.pickle; resuming the session restores them at kernel boot (functions, classes and modules are skipped — re-define those). /new starts fresh, /rlm restart wipes on purpose, ephemeral sessions (--no-session) skip persistence. Snapshots older than 30 days are garbage-collected.

Continual harness (/refine)

pi-rlm ports prime-agent's refinement loop: persistent memories (reusable facts/tactics) and policies (behavior notes) that are injected into the system prompt on every turn.

/refine list                              # show current entries
/refine prefer small diffs over rewrites  # refine now, session-scoped
/refine global always verify with tests   # refine now, cross-session

From inside the kernel: refine("create a memory about X", global_=True) — returns immediately, the refinement runs when the turn settles (never mid-cell).

A refinement spawns a tool-less child LLM that analyzes the recent trajectory and applies structured edits (create/update/delete). Local entries live in the pi session (survive /resume, branch with the session tree); global entries live in ~/.pi/agent/pi-rlm/harness.jsonl.

Recursion depth

Root agent = depth 0; each rlm() child is parent depth + 1. Requests beyond the budget are refused in-kernel (rlm() raises, the model adapts). Default max depth: 2.

| Where | How | |---|---| | per session | /rlm depth 3 | | per launch | pi --rlm --rlm-max-depth 3 | | environment | PI_RLM_MAX_DEPTH=3 (0 disables recursion) |

Commands & flags

| Command | Effect | |---|---| | /rlm | toggle RLM mode (persisted in the session) | | /rlm status | mode, depth, model for children, kernel stats | | /rlm restart | kill the kernel (next cell reboots with an empty namespace) | | /rlm depth <n> | session-scoped recursion budget | | /refine [global] [instructions] | run a harness refinement now | | /refine list | show harness entries | | --rlm | start in RLM mode | | --rlm-max-depth <n> | launch-scoped recursion budget |

Design notes

  • No ipykernel, no ZeroMQ. The extension owns both ends of a unix socket; the shim is ~250 lines of stdlib Python. Kernel boot is one python3 spawn, lazily on the first cell.
  • Cells are serialized; stream messages mirror stdout/stderr live to the tool row. Esc sends SIGINT (a KeyboardInterrupt traceback, kernel survives); a grace timer escalates to SIGKILL and the next cell reboots with a fresh namespace.
  • Sub-agents are child pi processes (--mode json -p --no-session --no-extensions -e <this extension> --rlm), the pattern from pi's subagent example. They inherit the session's model and may recurse until the depth budget runs out.
  • Nested usage accounting: child token/cost totals roll up onto the ipython tool result, so footer and /session totals include recursion.
  • Compaction: the kernel survives it; pi-rlm injects a namespace snapshot (variable names/types) after compaction so the model doesn't lose track of REPL state.
  • Snapshots: data survives restarts too — the namespace is pickled per session at shutdown and restored on resume.
  • rlm() results never enter the parent's context — only what cells print. That's the context-engineering point of RLM, and it falls out of the socket design for free.
  • Need packages? The kernel is stdlib-only by design, but nothing stops a cell from import subprocess; subprocess.run([sys.executable, "-m", "pip", "install", "--user", "pandas"]). A managed venv is deliberately out of scope.

What a fork still gets you (and this doesn't)

Honest list, for the prime-agent comparison: child sessions don't appear in pi's /tree or parent accounting UI (we roll usage up manually instead); no fork-server, so each child pays Python + pi cold start; child registry doesn't survive a daemon restart; per-cell cancellation is SIGINT-based and can't interrupt a blocked C call; harness entries cover memories/policies only (no skill/subagent kinds, no rollback history).

Development

npm install          # peers (pi packages) resolve for the typechecker
npm test             # LLM-free kernel protocol smoke test (python3)
npm run typecheck    # tsc --noEmit

End-to-end probes (uses your configured model):

pi -e ./src/index.ts -p --no-session "Use ipython twice: x = sum of squares 0..9, then x*2. Reply with the number."
PI_RLM_MAX_DEPTH=0 pi -e ./src/index.ts --rlm -p --no-session "Try rlm('hi'), tell me what happens"

Releasing

GitHub Actions publishes to npm on version tags (requires the NPM_TOKEN repository secret):

npm version patch        # bumps package.json, commits, tags vX.Y.Z
git push --follow-tags   # the tag triggers the publish workflow

Acknowledgments

  • Recursive Language Models — the RLM concept (Zhang et al.).
  • prime-agent (MIT, © Prime Intellect) — the product this ports to pi's extension API. pi-rlm reimplements the mechanism from scratch (different kernel architecture, rewritten prompts, own refinement store); no prime-agent code is copied.
  • pi (MIT, © Mario Zechner) — the platform; the child-process spawn pattern comes from pi's subagent example extension.

License

MIT