Skip to content

Python Tool and IPython Runtime

This document describes the current Python execution stack in packages/coding-agent. It covers tool behavior, kernel/gateway lifecycle, environment handling, execution semantics, output rendering, and operational failure modes.

Scope and Key Files

  • Tool surface: src/tools/python.ts
  • Session/per-call kernel orchestration: src/ipy/executor.ts
  • Kernel protocol + gateway integration: src/ipy/kernel.ts
  • Shared local gateway coordinator: src/ipy/gateway-coordinator.ts
  • Interactive-mode renderer for user-triggered Python runs: src/modes/components/python-execution.ts
  • Runtime/env filtering and Python resolution: src/ipy/runtime.ts

What the Python tool is

The python tool executes one or more Python cells through a Jupyter Kernel Gateway-backed kernel (not by spawning python -c directly per cell).

Tool params:

ts
{
  cells: Array<{ code: string; title?: string }>;
  timeout?: number; // seconds, clamped to 1..600, default 30
  cwd?: string;
  reset?: boolean; // reset kernel before first cell only
}

The tool is concurrency = "exclusive" for a session, so calls do not overlap.

Gateway lifecycle

Modes

There are two gateway paths:

  1. External gateway (PI_PYTHON_GATEWAY_URL set)

    • Uses the configured URL directly.
    • Optional auth with PI_PYTHON_GATEWAY_TOKEN.
    • No local gateway process is spawned or managed.
  2. Local shared gateway (default path)

    • Uses a single shared process coordinated under ~/.pisces/agent/python-gateway.
    • Metadata file: gateway.json
    • Lock file: gateway.lock
    • Spawn command:
      • python -m kernel_gateway
      • bound to 127.0.0.1:<allocated-port>
      • startup health check: GET /api/kernelspecs

Local shared gateway coordination

acquireSharedGateway():

  • Takes a file lock (gateway.lock) with heartbeat.
  • Reuses gateway.json if PID is alive and health check passes.
  • Cleans stale info/PIDs when needed.
  • Starts a new gateway when no healthy one exists.

releaseSharedGateway() is currently a no-op (kernel shutdown does not tear down shared gateway).

shutdownSharedGateway() explicitly terminates the shared process and clears gateway metadata.

Important constraint

python.sharedGateway=false is rejected at kernel start:

  • Error: Shared Python gateway required; local gateways are disabled
  • There is no per-process non-shared local gateway mode.

Kernel lifecycle

Each execution uses a kernel created via POST /api/kernels on the selected gateway.

Kernel startup sequence:

  1. Availability check (checkPythonKernelAvailability)
  2. Create kernel (/api/kernels)
  3. Open websocket (/api/kernels/:id/channels)
  4. Initialize kernel env (cwd, env vars, sys.path)
  5. Execute PYTHON_PRELUDE
  6. Load extension modules from:
    • user: ~/.pisces/agent/modules/*.py
    • project: <cwd>/.pisces/modules/*.py (overrides same-name user module)

Kernel shutdown:

  • Deletes remote kernel via DELETE /api/kernels/:id
  • Closes websocket
  • Calls shared gateway release hook (no-op today)

Session persistence semantics

python.kernelMode controls kernel reuse:

  • session (default)

    • Reuses kernel sessions keyed by session identity + cwd.
    • Execution is serialized per session via a queue.
    • Idle sessions are evicted after 5 minutes.
    • At most 4 sessions; oldest is evicted on overflow.
    • Heartbeat checks detect dead kernels.
    • Auto-restart allowed once; repeated crash => hard failure.
  • per-call

    • Creates a fresh kernel for each execute request.
    • Shuts kernel down after the request.
    • No cross-call state persistence.

Multi-cell behavior in a single tool call

Cells run sequentially in the same kernel instance for that tool call.

If an intermediate cell fails:

  • Earlier cell state remains in memory.
  • Tool returns a targeted error indicating which cell failed.
  • Later cells are not executed.

reset=true only applies to the first cell execution in that call.

Environment filtering and runtime resolution

Environment is filtered before launching gateway/kernel runtime:

  • Allowlist includes core vars like PATH, HOME, locale vars, VIRTUAL_ENV, PYTHONPATH, etc.
  • Allow-prefixes: LC_, XDG_, PI_
  • Denylist strips common API keys (OpenAI/Anthropic/Gemini/etc.)

Runtime selection order:

  1. Active/located venv (VIRTUAL_ENV, then <cwd>/.venv, <cwd>/venv)
  2. Managed venv at ~/.pisces/python-env
  3. python or python3 on PATH

When a venv is selected, its bin/Scripts path is prepended to PATH.

Kernel env initialization inside Python also:

  • os.chdir(cwd)
  • injects provided env map into os.environ
  • ensures cwd is in sys.path

Tool availability and mode selection

python.toolMode (default both) + optional PI_PY override controls exposure:

  • ipy-only
  • bash-only
  • both

PI_PY accepted values:

  • 0 / bash -> bash-only
  • 1 / py -> ipy-only
  • mix / both -> both

If Python preflight fails, tool creation degrades to bash-only for that session.

Execution flow and cancellation/timeout

Tool-level timeout

python tool timeout is in seconds, default 30, clamped to 1..600.

The tool combines:

  • caller abort signal
  • timeout abort signal

with AbortSignal.any(...).

Kernel execution cancellation

On abort/timeout:

  • Execution is marked cancelled.
  • Kernel interrupt is attempted via REST (POST /interrupt) and control-channel interrupt_request.
  • Result includes cancelled=true.
  • Timeout path annotates output as Command timed out after <n> seconds.

stdin behavior

Interactive stdin is not supported.

If kernel emits input_request:

  • Tool records stdinRequested=true
  • Emits explanatory text
  • Sends empty input_reply
  • Execution is treated as failure at executor layer

Output capture and rendering

Captured output classes

From kernel messages:

  • stream -> plain text chunks
  • display_data/execute_result -> rich display handling
  • error -> traceback text
  • custom MIME application/x-pisces-status -> structured status events

Display MIME precedence:

  1. text/markdown
  2. text/plain
  3. text/html (converted to basic markdown)

Additionally captured as structured outputs:

  • application/json -> JSON tree data
  • image/png -> image payloads
  • application/x-pisces-status -> status events

Storage and truncation

Output is streamed through OutputSink and may be persisted to artifact storage.

Tool results can include truncation metadata and artifact://<id> for full output recovery.

Renderer behavior

  • Tool renderer (python.ts):
    • shows code-cell blocks with per-cell status
    • collapsed preview defaults to 10 lines
    • supports expanded mode for full output and richer status detail
  • Interactive renderer (python-execution.ts):
    • used for user-triggered Python execution in TUI
    • collapsed preview defaults to 20 lines
    • clamps very long individual lines to 4000 chars for display safety
    • shows cancellation/error/truncation notices

External gateway support

Set:

bash
export PI_PYTHON_GATEWAY_URL="http://127.0.0.1:8888"
# Optional:
export PI_PYTHON_GATEWAY_TOKEN="..."

Behavior differences from local shared gateway:

  • No local gateway lock/info files
  • No local process spawn/termination
  • Health checks and kernel CRUD run against external endpoint
  • Auth failures are surfaced with explicit token guidance

Operational troubleshooting (current failure modes)

  • Python tool not available

    • Check python.toolMode / PI_PY.
    • If preflight fails, runtime falls back to bash-only.
  • Kernel availability errors

    • Local mode requires both kernel_gateway and ipykernel importable in resolved Python runtime.
    • Install with:
      bash
      python -m pip install jupyter_kernel_gateway ipykernel
  • python.sharedGateway=false causes startup failure

    • This is expected with current implementation.
  • External gateway auth/reachability failures

    • 401/403 -> set PI_PYTHON_GATEWAY_TOKEN.
    • timeout/unreachable -> verify URL/network and gateway health.
  • Execution hangs then times out

    • Increase tool timeout (max 600s) if workload is legitimate.
    • For stuck code, cancellation triggers kernel interrupt but user code may still need refactor.
  • stdin/input prompts in Python code

    • input() is not supported interactively in this runtime path; pass data programmatically.
  • Resource exhaustion (EMFILE / too many open files)

    • Session manager triggers shared-gateway recovery (session teardown + shared gateway restart).
  • Working directory errors

    • Tool validates cwd exists and is a directory before execution.

Relevant environment variables

  • PI_PY — tool exposure override (bash-only/ipy-only/both mapping above)
  • PI_PYTHON_GATEWAY_URL — use external gateway
  • PI_PYTHON_GATEWAY_TOKEN — optional external gateway auth token
  • PI_PYTHON_SKIP_CHECK=1 — bypass Python preflight/warm checks
  • PI_PYTHON_IPC_TRACE=1 — log kernel IPC send/receive traces
  • PI_DEBUG_STARTUP=1 — emit startup-stage debug markers