Introducing PiPy: a minimal Python agent harness for SuperQode

Coding agent harnesses are getting lot of traction at the moment and something interesting has been happening in the coding agent space. The frontier labs have spent it making their harnesses larger: more policy layers, approval stacks, sandboxing, and orchestration. This is not every users want or needed all the time. When an agent does something surprising inside one of these systems, working out which layer caused it becomes a research project. It could be harness or it could be model and if you are using the close source stack it becomes even harder. The smaller and tailored harnesses are picking up momentum and probably thats the reason the minimal harnesses like Pi are proving to be very effective as models gets smarter.

The harness is the product

An agent harness is the loop around the model. It decides what the system prompt looks like, which tools exist, how tool calls are executed, what happens to the transcript when it grows too long, and what the user can do mid run. The model is a component inside that loop. Swap the harness and keep the model, and the behaviour changes completely. The same model under two harnesses produces different quality of work, different failure modes, and different costs. Once you accept that, the harness stops being plumbing and becomes the interesting part of the system. Which raises an awkward question for the big vendor harnesses like Codex, Claude Code etc. If the harness is the product, and that harness is a closed loop with a dozen policy layers you cannot inspect, then you do not really own your coding setup. You are renting someone else’s opinion about how an agent should behave.

Why pi is getting ported

The minimal harness like Pi takes the opposite position. It is a small, event driven harness written in TypeScript, with a deliberately narrow tool surface and no policy stack. It runs with the permissions of the process that launched it. It does not try to be safe on your behalf. It tries to be legible. A harness you can read in an afternoon is a harness you can reimplement in your own language and extend for your own workflow. TypeScript is a fine home for the original, and seems like getting popular but it is not where most agent infrastructure gets written, and a harness this small does not need to stay in one ecosystem. There are now ports and reimplementations across several of them, including Go and Python, and Hugging Face has explored the same architecture in Tau. People are not porting it because it is fashionable but the design is small enough to port, and useful enough to be worth the effort.
SuperQode already allows developers to build own harnesses and its core harness is smaller than Pi and all those harnesses are built in Python. The AI/ML core research and papers are still written in Python and It is where the model tooling lives: the SDKs, the evaluation harnesses, the serving stacks, the notebooks people actually prototype in. A minimal agent loop written in Python drops into that ecosystem without a bridge process, and that is the gap PiPy fills.

The claim being made in the community is that this minimal approach holds its own against much larger harnesses on real coding work. We would encourage you to measure that yourself rather than take anyone’s word for it, including ours. What is not in dispute is the architectural argument: fewer layers between the model and the work means fewer places for behaviour to get lost. That’s whole purpose of the SuperQode, do not use any harness as it is instead build evaluate and optimize for your own coding use cases.

Enter PiPy

Please note that the PiPy is our own custom implementation of Pi like harnesses and its unofficial port. PiPy is our implementation of that architecture as a native harness inside SuperQode. It is an unofficial Python port of pi like agent harness, researched against pi-coding-agent, and it ships under the attribution recorded in NOTICE. pi is MIT licensed and we are grateful for it. It is roughly nine thousand lines of Python in superqode.pipy, and it is a first class harness rather than an external process we shell out to. PiPy sessions live in the same session store as everything else, so you can switch to PiPy mid conversation and switch away again without losing context.

What is actually in it

The core is an event first loop. Every meaningful thing that happens during a run is an event, not a return value.

from superqode.pipy import (
    AgentStartEvent, MessageStartEvent, MessageUpdateEvent,
    ToolExecutionStartEvent, ToolExecutionEndEvent,
    TurnStartEvent, TurnEndEvent, AgentEndEvent,
)

Building the loop this way is what makes the rest possible. Streaming, mid run steering, extension hooks and the session tree are all consumers of the same event stream, rather than special cases bolted onto a request and response model.The tool surface is intentionally small.

ALL_TOOL_NAMES = ("read", "bash", "edit", "write", "grep", "find", "ls")
CODING_TOOL_NAMES = ("read", "bash", "edit", "write")
READ_ONLY_TOOL_NAMES = ("read", "grep", "find", "ls")

PiPy has Seven tools without  plugin marketplace inside the harness or dynamic tool discovery. Tool calls in a batch execute concurrently where they can, and the loop resolves them together. Mid run steering is supported directly. The loop polls a steering queue between turns and after tool batches, so a message you send while the agent is working is picked up at the next safe point instead of waiting for the run to finish.

pending = await _poll_queue(config.get_steering_messages)

Sessions form a tree rather than a line. Every entry records its parent, so you can move back to an earlier point and take a different path without destroying what you already have. Forking a session copies it rather than branching in place. Context compaction is part of the harness rather than an afterthought, and the session commands cover the operations you actually need during a long piece of work.

/compact   Summarise older context and keep working
/tree      Move to another point in the session tree
/fork      Copy this session into a new one
/resume    Reopen a previous session for this directory
/model     Switch the model for the next turn
/export    Export the current branch as Markdown

The layering rule

PiPy has one architectural constraint that we enforce deliberately.

superqode.app -> superqode.harness -> superqode.pipy -> superqode.pipy.ai

Nothing inside superqode.pipy may import Textual, the approval manager, or the workbench tool registry. The harness does not know it is being run from a terminal user interface. It does not know SuperQode has an approval stack. This is what keeps the port faithful, and what keeps the loop testable without standing up an application around it. It also means PiPy is honest about what it is. It runs with the permissions of the process that launched it. There is no approval prompt and no sandbox layer between the agent and your machine. That is the whole point of this design. It is the right choice for some work and the wrong choice for other work, and SuperQode does not hide the difference: the harness row says so before you pick it.

Using it

From the connect screen, choose the harness route and pick PiPy.

:connect

Or switch to it directly at any point, including mid session.

:harness switch pipy

The session continues. Context is replayed into the new harness, so comparing PiPy against Core or Workbench on the same task is one command rather than a migration.

When you switch, SuperQode shows what you actually picked up.

    PiPy
      Runtime  pipy
      Tools    7: read, bash, edit, write, grep, find, ls
      Can      read, write, shell
      Sandbox  none
      MCP      none attached, add one with :mcp

PiPy does not bring its own model. Pick one after the harness, exactly as with any other SuperQode harness: a local engine, your own API key, or a plan you already pay for.

Watch Demo 

Extensions

The original pi is extensible, and a port that dropped extensibility would miss the point. We took a different route to the same outcome. Rather than porting pi’s TypeScript extension system, PiPy reuses SuperQode’s existing extension and hook system, so an extension you write works across harnesses instead of only inside this one. The bridge lives in superqode.harness.pipy_extensions and wires the hook registry onto PiPy’s harness events.

BRIDGED_HOOK_POINTS = (
    SESSION_START,
    USER_PROMPT_SUBMIT,
    BEFORE_TOOL_CALL,
    AFTER_TOOL_CALL,
    AFTER_TURN_COMPLETE,
    STOP,
)

An extension can observe a session starting, inspect or rewrite a prompt before it is submitted, inspect a tool call before it runs, block that tool call, observe the result, and act when a turn or the run completes. One hook point is deliberately not bridged: PERMISSION_REQUEST. That belongs to SuperQode’s approval stack, and PiPy runs with process permissions by design.

An extension can still refuse a tool call through BEFORE_TOOL_CALL, which is exactly what pi’s own extensions can do. The difference is meaningful: it is your extension making that decision, in code you wrote, rather than a policy engine making it for you. Attaching hooks returns unsubscribe callables, so a caller that rebuilds the harness detaches cleanly instead of stacking duplicate handlers.

unsubscribes = attach_extension_hooks(harness, hooks, session_id=session_id)

Why we implement PiPy

SuperQode already has harnesses like Pi e.g core harness with just 4 tools, workbench and other presets. It also have the no tool harness where harness reply on only model capability. SuperQode’s position is that you should be able to change the harness without changing everything else. Sessions, memory, evaluations and delivery belong to you, not to whichever agent you happen to be running this week. A harness is something you should be able to swap, compare, and eventually own.

PiPy is that argument made concrete. It is a minimal harness with no policy layers, sitting next to Core and Workbench, sharing the same session store and the same extension system, one command away from either. If the minimal architecture is as good as its proponents believe, you can now test that claim on your own repository against your own alternatives, and the comparison costs you a single command. If it turns out not to suit your work, switching back is equally cheap. That is rather the point.

Conclusion

Please note that this not an official port of the Pi but its Pi like implementation that allows your to use minimal harness in Python. It’s SuperQode’s own custom implementation like we have for other custom harnesses like core, workbench and presets for the models.

Try SuperQode and it’s different harnesses, evaluate it against your own repos and let us know what you think of the PiPy

curl -fsSL https://super-agentic.ai/superqode.sh | sh
superqode
:harness switch pipy

Then measure it.

:eval

Opinions about which harness is better are cheap. Scores on your own repository are not so measure the harness and build the custom one for your projects.