@hheei/pi-python-eval0.5.0
Persistent session-scoped Python eval for Pi, with nested tool calls through the Pi host.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@hheei/pi-python-eval
Persistent, session-scoped Python for Pi. One python_eval tool runs trusted local Python in a
kernel that keeps its namespace across cells, and lets a cell call other installed tools through the
Pi host.
Install @hheei/pi-settings separately for the Settings UI. @hheei/pi-ext-core is a production
dependency, not a separately loaded Pi extension.
Tool
python_eval({ code, reset?, timeout_ms? })
codeis non-empty trusted source, at most 1 MiB. This is not a sandbox.reset: truediscards the kernel and its scope before the cell runs.timeout_msis a hard wall-clock deadline for the whole cell, defaulting to 180000 ms (180 seconds). Set it explicitly to override the default. Nested tool calls count toward it.exposure: "model-only",defaultActive: false: enable it with--toolsordefaultTools.
Each cell reports its namespace, so names that survived are visible instead of assumed. print
and console go to the transcript, display() keeps a JSON-safe value, and the last expression is
the cell result. A restart is reported explicitly, because a kernel that died lost its names.
Nested tools
A cell reaches exactly read, grep, find, bash, edit, write, and apply_patch, and only
while that tool is active in the session. Every call goes through the Pi host's
context.executeTool(), so the host's own validation, hooks, and permission checks apply and the
host's reported outcome decides whether the call failed. The package holds no tool definitions and
no fallback dispatch, so a missing tool or a blocked call surfaces as a tool failure.
Nested calls return the tool's text, or its structuredContent when it declares one (for example
bash). Each call is recorded as a trace row with its arguments, text, duration, and error.
Tool calls are synchronous Python: result = tools.apply_patch({"patch": patch}). Do not use
await; top-level await is unsupported. A compile-time SyntaxError prevents the entire cell
from running, so earlier assignments in that cell do not create names. Fix and re-run the whole
cell, including those assignments. Runtime errors can leave earlier assignments intact; check the
reported namespace before retrying.
Kernel lifecycle
One kernel per extension instance, at most one cell at a time: a concurrent cell fails immediately
rather than queueing. Cancellation interrupts the cell (SIGINT) and escalates to SIGKILL after
about 5 seconds; session shutdown kills the kernel. Names never survive into another session.
The kernel runs src/kernel/child.py (packed beside the built host) over NDJSON on a private pipe,
so the child's stdout reaches the transcript instead of the protocol. Environment variables that
look like credentials (*_API_KEY, *_TOKEN, *_SECRET, *_PASSWORD) are not inherited.
Python 3.9+ is required.
Settings
Group eval in the global ext_settings.json, provider pi-python-eval.eval:
| Field | Default | Meaning |
|---|---|---|
pythonBin |
(auto) | Interpreter for the kernel. Empty uses python3 or python on PATH. |
Changes apply on reload or in a new session.
Details
Results carry format: "pi-ext-tools-python-eval" and bounded rows (200 rows, 4,000 characters per
row, 12,000 characters of model-visible transcript).
Rendering
createPythonEvalPresentation in this package owns the cell's rendering facts: container status,
summary ((reset), (timeout 2s), call stats, duration), the highlighted Python input section,
transcript sections (muted namespace report, display:/result: prefixes, one header row per nested
call), collapsed nested rows, and copy text. registerPythonEvalTool publishes it through
ext-core's optional renderer point, so an installed UI package draws the frame and this package's
native renderer shows the plain transcript when none is installed. No UI package is a dependency.