Local-first AI coding agent runtime: async backends (native + aider), persistent memory, MCP, fail-closed safety gates.
Project description
pxx 2.0 — local-first AI coding agent runtime
pxx is an async, event-sourced coding-agent runtime that runs against your own
inference (Ollama, vLLM, or any OpenAI-compatible endpoint) — no cloud dependency,
no telemetry, no API keys required. It pairs a native tool-calling agent loop with
persistent cross-session memory, deterministic safety gates, and MCP interop, and
can delegate to aider as an optional edit engine.
New here? → Hands-on tutorial
pxx 2.0 is a ground-up rewrite of pxx 1.x. The 1.x control-plane semantics (fail-closed gates, scope limits, bounded loops, audit) are preserved; the execution layer (
os.execvinto aider, argv scanning, sidecar services) is replaced by an async runtime where pxx owns the agent loop. See DESIGN.md for the architecture contract and docs/MIGRATION.md for 1.x → 2.0 changes.
Why pxx
- Offline-capable: all inference stays on your machine or your LAN.
- pxx owns the runtime: every model/tool event flows through pxx's event bus. Backends cannot bypass policy — scope, permissions, budgets, and hooks are enforced by the host, never by the model.
- Persistent memory: observations from previous sessions (files changed, tool
outcomes, your own
remembernotes) are stored in a local SQLite database with hybrid BM25 + vector search and injected deterministically at session start. - Fail-closed safety: read-only by default. Writes require
edit/runmode, stay inside a canonicalized scope (symlinks resolved), shell commands are gated, and every run ends with a machine-readable terminal code in a hash-chained audit log. - Interop: consumes MCP servers as tools, and exposes its own memory as an MCP server for other agents (Claude Code, goose, opencode, …).
Install
pip install pxx-orchestrator # the command is `pxx`
# optional extras:
pip install "pxx-orchestrator[aider]" # aider delegation backend (Python < 3.13)
pip install "pxx-orchestrator[server]" # headless HTTP API (pxx serve)
Prerequisites: Python 3.11+, and a reachable model endpoint —
Ollama by default (ollama pull qwen2.5-coder:7b).
Backends
pxx runs tasks on one of two execution backends, selected per run:
- native (the tool-calling agent loop this README describes):
pxxdrives the model directly through its own tool surface and gates. - aider (delegation): edits are handed to aider as the edit engine, still under pxx's scope/budget/hook gates.
Default selection: aider when the aider binary is on PATH, else native
(run/loop always default to native). Force one with
--backend native|aider on any run verb.
Tool calling. The native backend needs an endpoint that accepts tool calls
(tools in the chat-completions request). Ollama supports tool calling out of
the box. vLLM must be launched with --enable-auto-tool-choice --tool-call-parser <parser> — without those flags every native round fails
with HTTP 400 ("auto" tool choice requires …). pxx doctor probes for this.
ask/edit (and run with --backend aider) can sidestep via the aider
backend; pxx loop is native-only and cannot.
Quick start
pxx doctor # check your setup
pxx ask -m "Explain main.py" # read-only (default): no writes, no shell
pxx edit -m "Add error handling to main.py" # writes allowed, in scope
pxx edit --commit -m "…" # + commit the change on COMPLETED (opt-in)
pxx run -m "Add tests for utils.py" # unattended, budget-capped
pxx loop -m "Fix the failing tests" --scope src # bounded edit→test→review loop
pxx chat # interactive session
Edits land uncommitted by default so you can review them first — pass
--commit (or PXX_AUTO_COMMIT=1 / auto_commit = true) to have a COMPLETED
run commit its work. The pxx-pre/<ts> safety tag always points at the
pre-session HEAD, so undo is git reset --hard <tag> either way.
Permission modes: ask (read-only) → plan (plan only) → edit (writes in scope, shell via hooks) → auto (unattended, budgets enforced). Every run is bounded: max rounds/tokens/cost/wall-clock/diff-lines, all configurable.
New to pxx? The hands-on tutorial takes you from install to building (and safely undoing) a small tool in ~25 minutes — the fastest way to get the mental model: read-only by default, scoped edits, and the safety tag that nets your work.
Memory
pxx memory add "we use ruff, not black" --tags conventions
pxx memory search "linting"
pxx memory list
Memory is hybrid-retrieved (FTS5 BM25 0.4 + embedding cosine 0.6). Embeddings come from a local Ollama model when reachable, else a deterministic hash embedder — search always works offline. TTL'd observations archive to JSONL monthly. Memory is context, never policy.
Memory is project-scoped by working directory: search/list see only the
current directory's project (its directory name) — run them from the directory
the memory was added in. Keyword search matches whole tokens exactly (no
stemming): searching round will not match rounding.
Expose it to other agents over MCP:
pxx mcp # stdio MCP server: memory_search / memory_add / memory_list
Configuration
Layered, highest precedence wins: CLI flags → PXX_* env → ./pxx.toml (or
.pxx/config.toml) → ~/.config/pxx/config.toml → defaults. Unknown keys are
rejected (fail-closed, no silent typos). Example pxx.toml:
model = "qwen2.5-coder:14b"
provider = "ollama"
permission = "edit"
scope = ["src", "tests"]
test_command = "pytest -q"
[budgets]
max_rounds = 20
max_cost_usd = 2.0
[[fallback_models]]
model = "served-model"
provider = "vllm"
base_url = "http://gpu-box:8000"
[[hooks]]
event = "PreToolUse"
command = "/usr/local/bin/my-gate" # exit 0 allow / 2 deny — deterministic
[[mcp_servers]]
name = "filesystem"
command = ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."]
1.x PXX_OLLAMA_BASE / PXX_OLLAMA_MODEL env vars and ~/.config/pxx/env
still work.
Headless API
pxx serve --port 8400 # FastAPI: POST /v1/sessions, SSE event stream,
# cancel, memory proxy. Loopback-only by default.
Operator commands
Beyond the everyday verbs (ask/edit/plan/run/loop/chat):
Safety & release
pxx check [--all-files] # secret/PII scan — staged files, or all tracked files
pxx upgrade # upgrade the pxx install in place
pxx review [--staged|--since SHA] # read-only review of the current diff (exit 2 on REVISE)
pxx doctor # diagnose setup (endpoints, backend, memory, config)
pxx audit verify <path> # verify a hash-chained audit log
Run evidence (every run is recorded with an immutable agent manifest)
pxx runs list|show|export # recorded runs, per-agent projections
pxx runs resume <run-id> # resume a run from its checkpoint
pxx agents list|show # agent versions + success rates (drift quarantine)
pxx verify [run-id] # verification packet for a run (gates fired)
pxx metrics summary|failures|memory-impact|export|compare
Evaluation & improvement (the self-improvement platform)
pxx eval run|self-check|report [--partition held-out]
pxx calibrate # reviewer calibration (recall/fp/agreement)
pxx improve analyze|clusters|proposals|cycle|status|daemon|pause|resume
pxx improve evaluate-candidate <id> # held-out, both arms
pxx improve readiness|auto-promote|principles
pxx propose # create a constrained improvement candidate
pxx compare <baseline> <candidate> # promotion verdict (held-out, multi-metric)
pxx promote <candidate-id> # human-gated promotion (needs a real scorecard)
pxx agent activate|rollback|history|channels|canary
pxx goal -m "<goal>" # goal -> task DAG -> isolated per-node loops
Legibility (docs/workflow contracts)
pxx workflow validate # validate this repo's WORKFLOW.md
pxx context audit # docs present + trust mirrors in sync
pxx docs check # every documented verb exists
Every verb self-describes: append --help (e.g. pxx check --help).
Safety model (short version)
- Edit-capable sessions (
edit/run/loop/goal, in a git repo) tie a safety net before anything can write: uncommitted work is stashed (--include-untracked, message carries the run id) and HEAD is taggedpxx-pre/<ts>. Restore withgit reset --hard <tag>+git stash pop— pop is your move, never pxx's. Disable withsafety_net = false. - Paths are canonicalized with symlinks resolved before any gate decision — model output never defines the trust boundary.
- Hooks are deterministic gates (like Claude Code's PreToolUse): they cannot be overridden by model judgment.
- The audit log (
~/.local/state/pxx/audit/YYYY-MM-DD.jsonl) is hash-chained and metadata-only — no prompts, file contents, or secrets. Verify withpxx audit verify <path>. - Bounded loops stop on: round cap, diff cap, budget, scope violation,
non-monotonic test progress (
NO_TEST_PROGRESS), a detected oscillation (LOOP_DETECTED), or a blocking review verdict.
Upgrading
With 2.0 on PyPI:
- uv tool:
uv tool upgrade pxx-orchestrator - pipx:
pipx upgrade pxx-orchestrator - pip:
pip install -U pxx-orchestrator - from source:
git pull && uv sync --extra dev --extra server - in-place:
pxx upgrade— upgrades the pxx install (detects uv tool / pipx / pip automatically)
Settings, memory, and audit state carry forward — 2.0 migrates them on first run (see docs/MIGRATION.md).
Development
git clone https://github.com/cdnwetzel/pxx && cd pxx # the 2.0 tree (branch v2)
uv sync --extra dev --extra server
uv run pytest # 870+ tests, no network/Ollama/aider required
uv run ruff check
2.0 lives on
cdnwetzel/pxx(this repo); the 1.x line continues on itsv1.xbranch. The public history is a curated series — the full development history stays private.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pxx_orchestrator-2.1.1.tar.gz.
File metadata
- Download URL: pxx_orchestrator-2.1.1.tar.gz
- Upload date:
- Size: 311.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6bedb1fdcc7d1f36bd84266015c7492f804c04db8b2e51d4479fa5fa84f8d008
|
|
| MD5 |
681f90fa37d8867774d8e95a8e02b0e0
|
|
| BLAKE2b-256 |
8cc728e0f322ba8662efc12a31a142318ca88103b218c05c092781183261fc2c
|
Provenance
The following attestation bundles were made for pxx_orchestrator-2.1.1.tar.gz:
Publisher:
release.yml on cdnwetzel/pxx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pxx_orchestrator-2.1.1.tar.gz -
Subject digest:
6bedb1fdcc7d1f36bd84266015c7492f804c04db8b2e51d4479fa5fa84f8d008 - Sigstore transparency entry: 2256595811
- Sigstore integration time:
-
Permalink:
cdnwetzel/pxx@ad5c3d648536d6cfa3fd2cee268b8d32108c36bd -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/cdnwetzel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ad5c3d648536d6cfa3fd2cee268b8d32108c36bd -
Trigger Event:
push
-
Statement type:
File details
Details for the file pxx_orchestrator-2.1.1-py3-none-any.whl.
File metadata
- Download URL: pxx_orchestrator-2.1.1-py3-none-any.whl
- Upload date:
- Size: 222.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53ad2f82fbbde39308800b1c4038c49308ed1f421124366486dce632c9b438b2
|
|
| MD5 |
b15afea1a4c603d719f31de84f979745
|
|
| BLAKE2b-256 |
8b325ef9fed1fc15b18326048160c30248d8e45cce576a7055d15bdd7cd6d3f1
|
Provenance
The following attestation bundles were made for pxx_orchestrator-2.1.1-py3-none-any.whl:
Publisher:
release.yml on cdnwetzel/pxx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pxx_orchestrator-2.1.1-py3-none-any.whl -
Subject digest:
53ad2f82fbbde39308800b1c4038c49308ed1f421124366486dce632c9b438b2 - Sigstore transparency entry: 2256595818
- Sigstore integration time:
-
Permalink:
cdnwetzel/pxx@ad5c3d648536d6cfa3fd2cee268b8d32108c36bd -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/cdnwetzel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ad5c3d648536d6cfa3fd2cee268b8d32108c36bd -
Trigger Event:
push
-
Statement type: