- Added IPython-backed Python tool with streaming output and image/JSON rendering. - Implemented Jupyter kernel gateway integration with WebSocket communication. - Added Python prelude with 30+ shell-like utility functions for file operations. - Migrated environment variables from PI_ to OMP_ prefix with automatic migration. - Added streaming output system with automatic spill-to-disk for large outputs. - Reorganized settings interface into behavior, tools, display, voice, status, lsp, and exa tabs.
8.0 KiB
8.0 KiB
Python REPL via IPython kernel: plan
Assumptions
- No security/sandboxing requirements.
- No persistence across sessions; kernel lifetime is scoped to the agent session (or explicit reset/close).
- REPL is per agent session (not shared across agents unless explicitly wired).
- Python available locally; IPython/Jupyter kernel deps can be added as runtime deps later.
- Streaming output to TUI should mirror bash tool behavior (chunked, truncation, expandable output).
Goals
- Add a Python REPL tool backed by IPython kernels.
- Integrate with existing bash tool surface (schema/renderer/streaming/truncation).
- Reuse bash-executor patterns for streaming, cancellation, and output capture.
- Define adapter interfaces to support local and future remote kernel execution.
Key integration points
packages/coding-agent/src/core/tools/bash.ts
- Pattern to follow: tool schema, execute handler, streaming updates, truncation, error surfacing, renderer.
- Target additions:
- A new tool module
python.ts(orpyrepl.ts) mirroring bash tool structure. - Similar
ToolDetailswith truncation metadata, fullOutputPath, fullOutput. - Reuse
truncateTail+ TUI renderer patterns.
- A new tool module
- Adapter use: inject a
PythonOperations(analogous toBashOperations) to allow local vs remote kernel implementations.
packages/coding-agent/src/core/bash-executor.ts
- Patterns to reuse:
BashExecutorOptionsfields:cwd,timeout,onChunk,signal.BashResultshape for output/truncation/cancelled.createOutputSinkandpumpStreamfor streaming + truncation.
- Target additions:
- A parallel module
python-executor.tsor genericstream-executor.tsthat wraps kernel I/O to matchBashResultsemantics. - Option to share
createOutputSinklogic (split into shared module) if preferred.
- A parallel module
Kernel lifecycle
Lifecycle states
- Provisioning: start kernel process + open IPC channels.
- Ready: kernel info request succeeds; execute requests accepted.
- Executing: process
execute_requestand stream outputs. - Interrupted (optional): on timeout or cancellation; send interrupt.
- Shutdown: request kernel shutdown; clean IPC; kill if unresponsive.
Lifecycle management
- Kernel is created at tool invocation start (or on first execution if implementing a multi-call session).
- Kernel must be shut down on:
- successful completion
- error (startup failure, exec failure)
- abort signal
- timeout
- Use a
KernelControllerinterface to own lifecycle and cleanup.
IPC strategy
IPython/Jupyter protocol
- Use ZeroMQ sockets:
shell,iopub,control,stdin,hb. - Send
kernel_info_requestto verify readiness. - Execute with
execute_requestonshellchannel. - Stream results from
iopub:stream(stdout/stderr)execute_result(repr, display data)error(traceback)status(idle/busy)
- Treat
status: idleas execution completion signal.
Session IDs
- Generate
session_idandmsg_idper execute call. - Filter iopub messages by
parent_header.msg_idto avoid cross-talk.
Bun integration
Process spawn
- Use
Bun.spawn()for local kernel process:python -m ipykernel_launcher -f <connection-file>. - Allocate free ports, then use
Bun.file()andBun.write()to create the connection file (JSON) before spawn. - Use
AbortSignalto cancel execution and trigger interrupt/termination.
ZMQ handling
- Use a JS ZMQ library compatible with Bun (investigate
zeromqBun support). - If Bun is incompatible, consider a tiny Node sidecar for ZMQ until Bun support is verified (documented in plan, not implemented now).
Tool availability settings
- Add a setting to control tool exposure:
bash-only,ipy-only, orboth. - Default:
ipy-onlywith automatic fallback tobash-onlyif kernel startup fails. - Tool registration should consult settings and runtime availability per session.
Shell bridge in Python
- Provide a
bash()helper in the Python prelude that shells out viabash -lcand uses the snapshot path when available. - Reuse the existing TypeScript
shell-snapshot.tsto generate the snapshot file, then pass its path to the kernel via an env var. - Python helper should prefer the snapshot env var if present; otherwise run plain
bash -lc.
Execute flow
- Create kernel controller (spawn process + load connection file).
- Open ZMQ sockets and send
kernel_info_request. - Send
execute_requestwith code. - Collect output:
stream→ append to outputexecute_result/display_data→ serialize as text/plain or JSON texterror→ include traceback, mark as failure
- Complete when
status: idlefor matchingparent_header.msg_id. - Apply truncation rules and return tool result.
- Shutdown kernel.
Streaming to TUI
- Same mechanics as bash tool:
- incremental
onUpdatewith truncated tail - final output includes truncation metadata and
fullOutputPath
- incremental
- Use
truncateTailfor preview +fullOutputin details for expansion. - For display data, prefer text/plain; fallback to JSON string.
Adapter design
Interfaces
export interface PythonKernelOperations {
startKernel(options: { cwd: string; env?: Record<string, string> }): Promise<KernelHandle>;
sendExecute(handle: KernelHandle, code: string, options: ExecuteOptions): AsyncIterable<KernelMessage>;
interrupt(handle: KernelHandle): Promise<void>;
shutdown(handle: KernelHandle): Promise<void>;
}
export interface KernelHandle {
id: string;
connectionInfo: KernelConnectionInfo;
}
export interface KernelConnectionInfo {
transport: "tcp" | "ipc";
ip: string;
shellPort: number;
iopubPort: number;
controlPort: number;
stdinPort: number;
hbPort: number;
key: string;
signatureScheme: string; // e.g. "hmac-sha256"
}
export interface ExecuteOptions {
signal?: AbortSignal;
timeoutMs?: number;
onChunk?: (text: string) => void; // sanitized
}
Executor adapter
- Create
executePythonthat mirrorsexecuteBash:- input: code +
PythonExecutorOptions(cwd,timeout,signal,onChunk) - output:
PythonResultmirroringBashResult
- input: code +
- Use a shared
createOutputSink(move tostreaming-executor.ts).
Mapping to existing executor shape
PythonExecutorOptionsmirrorsBashExecutorOptionsfor compatibility.PythonResultmirrorsBashResultfor renderer reuse.- Tool details/truncation identical to
bash.ts.
Tool surface (schema)
command→code(string)timeout(seconds)workdir(optional)kernel(optional): for future extension (e.g., python version); unused now.
Concrete implementation steps (design plan)
- Design shared streaming utilities
- Factor
createOutputSinkandpumpStreamintostream-executor.ts. - Keep bash-executor using it to avoid duplication.
- Factor
- Define kernel operations interface
- Create
PythonKernelOperations+KernelHandletypes. - Provide default local implementation using Bun + ZMQ.
- Create
- Implement python executor
executePython(code, options)to stream outputs and returnPythonResult.- Handle timeouts → interrupt kernel + annotate output.
- Implement python tool
- Mirror bash tool behavior: interception not needed.
- Use
executePythonwith onUpdate streaming + truncation. - Renderer can reuse
bashToolRendererlogic or clone with label changes.
- Integrate into tool registry
- Add new tool to tools index with appropriate schema.
- Ensure tool description prompt is written (new prompt file if required).
- Wire into TUI
- Ensure
tool-executionsupports expansion via details.fullOutput.
- Ensure
- Add unit tests
- Mirror
prompt-templatestests; add REPL output/truncation tests.
- Mirror
Notes for docs/ipy-*.md
Include the following sections:
- Motivation + non-goals
- Kernel lifecycle diagram
- IPC message flow (execute_request → iopub stream/error/result → status idle)
- Executor interface and data structures
- Tool schema and output format
- Timeout/cancel behavior
- TUI streaming and truncation handling
- Future extensions (persistent kernels, multiple executions, rich display handling)