Repository navigation
Releases
pi-python v0.9.1
This is a patch release. It has one fix: provider = "openai" now shows the thinking that a gateway streams as reasoning_content. Fixed has the details. The VS Code extension needs a new agent binary to get the fix, so it needs a new Release and a new VSIX. Changed has the new version numbers. Known issues says that the 0.9.0 list still holds.
Changed
- New version numbers (all Python packages, TUI):
pi-agent-core-lc,pi-agent-harness-lcandpi-agent-cli-lcgo from 0.9.0 to 0.9.1. The three pin each other with==, so install them at one version. Onlypi-agent-core-lchas a code change.pi-agent-harness-lcandpi-agent-cli-lcchange only their version and their pins, because PyPI skips a version that exists, and 0.9.0 of the two pins core==0.9.0. The four extension packages need the new core (pi-agent-core-lc>=0.9.1, andpi-dynamic-workflows-pyalsopi-agent-harness-lc>=0.9.1) and get new versions, because the publish step skips a version that PyPI has already:pi-web-access-py,pi-goal-x-pyandpi-dynamic-workflows-pygo from 0.6.0 to 0.6.1, andpi-mcp-pyfrom 0.4.0 to 0.4.1. They have no code change.zypi --versionand theagentInfoof the agent show 0.9.1 (the TUI cratespi-pager-binandpi-version, andtui/Cargo.lock, carry the same number). The TUI has no code change.
Fixed
provider = "openai"shows the thinking that a gateway streams (pi-agent-core): a gateway that serves a reasoning model (LiteLLM, vLLM, SiliconFlow, OpenRouter) puts the thinking indelta.reasoning_content(OpenRouter and newer vLLM:delta.reasoning).langchain_openai.ChatOpenAIdoes not read these fields, so the adapter never got any thinking and a client showed none: nothinking_deltaevent, nothinkingblock in the session file.ChatDeepSeekkeeps them, but thezycode-agentbinary (the VS Code extension, the registry archives) has nolangchain_deepseek, andprovider = "openai"with abase_urlis the one way to reach a gateway there. Found in the VS Code extension: sessions of one model withprovider = "deepseek"had thinking blocks, and sessions withprovider = "openai"had none. Theopenaiprovider now builds a small subclass ofChatOpenAI(adapters/openai_reasoning.py) that puts the thinking of a streamed chunk intoadditional_kwargs["reasoning_content"], asChatDeepSeekdoes, with the same two field names in the same order. A chunk with no such field is not touched, so api.openai.com does not change. The request does not change: the thinking is not sent back (provider = "deepseek"still does that on DeepSeek's own API), and athinkingblock of an earlier turn is left out of the request as before. A session that now holdsthinkingblocks replays withprovider = "openai"without error (test: the request of a history with thinking and a tool call). Tests:pi_agent_core/tests/test_openai_reasoning.py; with the change taken out, four of them fail, and the first turn of the stream test has atoolCallblock and nothinkingblock, as in the session files. Verified with a streamed answer through the realChatOpenAIclass and a fake HTTP client. Not verified: against a live gateway, and in a builtzycode-agentor a VSIX (the extension needs a new agent binary: a new Release and a new VSIX). A thinking that is not streamed (a call that is notastream) is not covered; the adapter always streams.
Known issues
The known issues of 0.9.0 still hold. This release changes none of them.
pi-python v0.9.0
This release is about Auto. permission = "auto" is a real permission mode now: it runs the calls that are clearly in scope and asks about the rest. The Python agent runs it, and zypi offers it. A bash command that no rule decides goes to a model by default. Changed has the changes that you can see: permission = "auto" no longer means "no question", Auto sends your messages and that command to a model, and zypi keeps pasted images in a new folder. Fixed has two security fixes for holes that v0.8.0 has (git status at the start of a session, and the isolation of the workflow tool), and a fix for new installs that got agent-client-protocol 1.0.0 and did not start. Removed has more dead code of the TUI. Known issues says what does not work yet and what nobody tried by hand.
Added
- The README tells how to run zycode as a subagent of DeepSeek Harness (docs): the new section "In DeepSeek Harness (as a subagent)" has the install command for
@deepseek-ai/dsh-subagent-acpand@deepseek-ai/dsh-tool-subagent(alpha), the two config rows, the permission behaviour, and a warning:permission: allowin the DSH row answers zycode's project-trust question with "Trust and remember", so keeprejectand setdefault_project_trust = "never"in the[extensions]table of an isolatedPI_HOME. Verified end to end with a real model (OpenRouterdeepseek/deepseek-v4.1-flash: one read task, one write task withpermission = "auto"; DSH main model and zycode child both real) and with a mock model for the wiring. It was run with DSH 0.2.2-alpha.2 (headlessprofile) andpi-agent-cli-lc0.7.0 on Linux: install, spawn, handshake, the result in DSH's completion notice, cwd, read and write through a scripted tool call, a denied and an allowed permission request, SIGTERM and SIGKILL ofdsh(the child's tool process is reaped), the first-run message, and the trust hazard and its mitigation. Not tried: the[deepseek]extra, a long real task, a colduvxdownload, DSH Web/Desktop, Windows and macOS. No code changes. - The README tells how to run zycode as a full session in DeepSeek Harness through a community plugin (docs): the new section "In DeepSeek Harness (full session, community plugin)" covers the third-party plugin
@zaimokuza/dsh-acp-adapter, with an unofficial notice (DeepSeek has not reviewed it, it does not list zycode as tested). It has the install commands, the agent row (command: uvx, an isolatedPI_HOME,loginHint), and notes on key, first run, permissions, project trust, models, resume, cancel and the MCP notice. Verified end to end with a real model (OpenRouterdeepseek/deepseek-v4.1-flashfor DSH's main model and for zycode) in the DSH Web app on Linux, with DSH0.2.1-rc.4, adapter0.2.1-rc.4.1(the newest adapter, pinned to exactly that DSH; the installer refuses it on0.2.1-rc.5) andpi-agent-cli-lc0.8.0: a read task, abashand awriterequest that were rejected and allowed on DSH's permission card, the project-trust question with its original options (nothing answered for you), a model switch in a session, resume after a restart ofdsh, and Stop with a runningbashcommand. The plugin does not offer a terminal login, so zycode gets no login there and the first run needs--setupin a shell. Not tried: other DSH versions, the Desktop app, Windows and macOS,session/load, zycode withpi-mcp-py. No code changes. - Auto is a real permission mode in the Python agent: it runs the calls that are clearly in scope and asks about the rest (
pi-agent-cli):permission = "auto"inagent.toml, or the newmodeitem of a session (see below), turns on rules that judge each tool call before it runs. These run with no question: a tool that declares itself harmless (read,grep,find,ls,fetch_url,web_search; the same test as inask), awriteoreditinside the project folder (a new file, or a file in a git work tree), and abashcommand that the parser shows to be read-only. These ask, and the question holds one fixed sentence that says why, for examplebash (Auto: leaves the project folder)(it never holds command text or a file name): a path outside the project folder or on the protected list, a delete or an overwrite that git cannot undo, network use that sends or installs, secrets, a command that hides what it does (eval,$(...),python -c, here-documents), project code in a project that is not trusted, and a tool that declares nothing (a third-party tool, an MCP tool,workflow). Auto never refuses a call by itself: the outcomes are "run" and "ask", and an error, a timeout or a state that Auto cannot read is an "ask". Abashcommand that the rules cannot show to be safe goes to the model step (see the entry about it below), or asks the user when[auto] classifier = "rules". The[auto]table of the homeagent.tomlhasclassifier,model,timeout_seconds,extra_roots,protected,bash_timeout_secondsandbash_max_output_mb;agent.example.tomlshows them.zypioffers Auto: its Shift+Tab ring and its settings page haveask,autoandalways-approve(see thezypichange below). Design, decisions and test plan:docs/specs/2026-11-02-auto-mode-design.md. - Auto runs the calls of one message one by one, and checks the session before each call (
pi-agent-cli,pi-agent-harness): the rules judge a call on the files as they are at that moment. The harness used to judge all calls of one assistant message first and run them after, so awritecould change the files that the next call was judged on (a link that the first call made, for example). While a session is in Auto, the order is decide, run, decide, run:AgentHarnesshas a new attributetool_executionand the methodsget_toolandget_tools, and Auto sets the mode tosequentialand puts it back when the session leaves Auto. A call that is judged while an earlier call of its message is still open asks. Aworkflowsub-agent whose calls pass a gate runs them one by one too. The rules also trust a context that they cannot check themselves, so Auto checks it when it starts and before each call: the project folder is absolute and exists; the home folder,PI_HOME, everyextra_rootsentry and every protected path are absolute; a tool is one of ours (bash,write,edit) by the identity of the tool object, not by its name, so a tool that an extension registers asbashalways asks;PATHhas no empty or relative entry; and the Bash tool runs bash. While a check fails, every call that does not declare itself harmless asks, and the user is told what is wrong when the session starts. A value of[auto]that Auto cannot use is told at the start of a session in any mode. - Auto asks before a write to a file that something else runs later (
pi-agent-cli):.husky/*,.githooks/*,.claude/*,.cursor/*,.mcp.json,.vscode/tasks.jsonand.github/workflows/*are on the default protected list (auto_rules.DEFAULT_PROTECTED_GLOBS, one constant; a test covers each entry).Makefile,package.jsonandconftest.pyare not on it: the agent changes them in ordinary work, and a question each time would make Auto useless.[auto] protectedin the homeagent.tomladds globs. An entry that is not text makes Auto ask about every call until the file is right, because a bad entry would otherwise loosen the list. - A
bashcall that Auto allowed has a time limit and an output limit (pi-agent-core,pi-agent-cli): 300 seconds and 64 MB by default ([auto] bash_timeout_seconds,bash_max_output_mb). The shorter of the limit and the model's own timeout wins. A call that the user allowed after a question has none: the user chose it. The core has a newBashLimitsandcreate_bash_tool(limits_for_call=); without it the tool does not change. - Auto keeps a record, and
/auto-logshows it (pi-agent-cli): each decision is api.auto_decisionentry of the session file (tool call id, tool, outcome, who decided, reason code, rule id, milliseconds; no command text and no file name). The calls that a tool declares harmless are not recorded./auto-logshows the counts and the last 20 decisions, and it exists in every session. - The mode belongs to the session, and a client can change it with the standard items (ACP,
pi-agent-cli):session/new,loadandresumereturn amodeconfig option (categorymode;askandauto) andmodes, and a client changes the mode withsession/set_config_optionorsession/set_mode.always-approveshows as the current value of a session that is in it, and a client cannot choose it with these items. The legacy items of the pager still work:_meta.autoModeand_meta.yoloModein the session requests (a flag that is false or missing says nothing), and the notificationpi/yolo_mode_changed, which has no session id and so moves every open session and sets the start mode of sessions that open later. A session that enters Auto gets a message that says what runs, what asks, and what Auto does not guard. - Auto is not a sandbox (docs): Auto guards what a call writes or runs. It does not guard what a call reads or sends.
read,grep,find,ls,fetch_urlandweb_searchrun with no question for any path or URL, and so does a recursive or glob read insidebash(grep -r token .,rg --hidden -uuu,git grep,git log -p,cat *). Such a call can show a secret to the model, andfetch_urlcan send it to a server (the old gap ofaskmode, kept as it is). The boundary is a sandbox (zypi --sandbox, a container, a VM). The rootAGENTS.md(invariants 9b to 9d),packages/pi-agent-cli/AGENTS.md,README.md,docs/TUI-AND-CODE-AGENT.md,agent.example.toml, the entry message of Auto and the design record say so. - Auto asks a model about a
bashcommand that no rule decides (pi-agent-cli): `auto_classi...
pi-python v0.8.0
This release is about the agent as a program that needs no Python. The agent can now run as a frozen (PyInstaller) executable, and the new workflow agent-binaries.yml builds one executable for each of five platforms and attaches the archives to the GitHub Release of a tag. These archives are the base of the binary entry for the ACP registry (the entry is not submitted yet). pi-agent-cli-lc now installs the OpenAI-format provider (langchain-openai) by default, so a bare uvx pi-agent-cli-lc can call a model. Changed also has two changes of zypi that you can see: it refuses 21 flags that the Python agent does not back, and its settings page has eight rows fewer. Known issues says what does not work yet, and that includes a macOS finding about the workflow sandbox.
Added
- The agent can run as a frozen executable (PyInstaller): three places in the code assumed a Python interpreter next to the agent. The
workflowsandbox started its script process withsys.executable -I -S -B -X utf8 child.py, which a frozen program refuses. A frozen build now starts a program of its own for that,child.pyfrozen alone with the standard library and nothing else, from the folderfrozen-childnext tosandbox/host.py. The layers of the sandbox do not change. The older form of the terminal login (_meta["terminal-auth"]) namedsys.executable -m pi_agent_cli --setup; a frozen build names its own program and--setup. A provider that a frozen build does not hold (anthropic,deepseek, any other) now fails with a text that says to setprovider = "openai"andbase_url, not withpip install ..., which cannot help there.--setupoffers only the providers that the build holds.PI_SANDBOX_FROZEN_CHILD=<program>runs the whole sandbox test suite against the frozen program. Tests:pi_agent_core/tests/test_frozen_providers.py,packages/pi-dynamic-workflows/tests/test_sandbox_frozen.pyandconftest.py, and additions totest_acp_auth_methods.pyandtest_setup_command.py. - A workflow builds the agent as one executable for each of five platforms (CI, packaging):
.github/workflows/agent-binaries.ymlbuilds the agent with PyInstaller (one file) onubuntu-22.04,ubuntu-22.04-arm,macos-15,macos-15-intelandwindows-2022. There is nowindows-aarch64build, becausetiktokenhas no wheel for it. Each program is tested on its own platform: a smoke test, 13 ACP contract tests against the program, and the sandbox tests against the frozen script process. The archives are namedzycode-agent-<target>-<version>.tar.gz(.zipfor Windows). The workflow starts after theReleaseworkflow succeeds for a tag push (workflow_run) and uploads the five archives to that release withgh release upload --clobber. It stops when the tag is notv<version of pi-agent-cli-lc>. A pull request that touches the agent runs it as a dry run, and by hand (workflow_dispatchwith atag) it repairs a release whose upload failed. The frozen agent holds only the OpenAI-format provider; for Anthropic or DeepSeek useprovider = "openai"withbase_url, or the Python package. Not signed. Code:packaging/agent/. Design, release steps and trial results:docs/specs/2026-10-31-agent-binaries.md. Not tried yet: thepublishjob (it runs only after a tag), the rule of GitHub that aworkflow_runworkflow uses the file on the default branch, and the first start of a program in Zed or JetBrains. - The Landlock layer of
pi-sandboxis tested on a Landlock kernel, and the flags and settings ofzypiare measured against the Python agent (TUI, tests): the new testlandlock_enforcement_e2e(workspace, read-only and strict profiles; the parent stays free; a child inherits the layer) passed on kernel 6.12 (Landlock ABI 6). The probes for flags and settings (scripts/tui_pty/) are the base of the two changes ofzypibelow. Test code only:zypidoes not change by these. (#57, #58)
Changed
-
pi-agent-cli-lcdepends on the OpenAI-format provider (langchain-openai): it asks forpi-agent-core-lc[openai]. A registry entry cannot add--with, so a bareuvx pi-agent-cli-lchad no provider and could not call a model. The other providers are extras of the package now:pi-agent-cli-lc[anthropic]andpi-agent-cli-lc[deepseek](the extras ofpi-agent-core-lcstay). Behaviour change: installingpi-agent-cli-lcnow also installslangchain-openaiand its dependencies (openai,tiktoken). -
An interactive
zypirefuses a flag that the Python agent does not back, and says why (TUI): an interactive session passes no flag to the agent, so 21 flags that came from the upstream project did nothing:--allow,--deny,--reasoning-effort,--worktree,--max-turns,--disable-web-searchand 15 more.--denywas the worst case. The agent still asked about a call that matched the rule, and it never refused the call. Nowzypiprintserror: zypi did not start., names each of these flags with its reason, and exits with status 2 before it starts the agent. 19 of the flags are no longer inzypi --help.--rulesand--system-prompt-overridestay there with the note "-p only", because-ppasses them to the agent. An interactive session reads.pi/SYSTEM.mdand.pi/APPEND_SYSTEM.mdinstead.-p, the subcommands and the hidden compatibility flags do not change. Behaviour change: a script that starts an interactivezypiwith one of these flags now fails with status 2. -
The settings page does not offer eight settings that change nothing (TUI):
remember_tool_approvals,default_selected_permission,default_model,toolset.ask_user_question.timeout_enabled,fork_secondary_model,prompt_suggestions,hunk_tracker_modeandauto_updatesteered the Rust runtime, or a feature that left with it. With the Python agent, a change on the page did nothing, or it did something other than the row said.default_modelsaved a key that the next start ignores.default_selected_permissionshowed "Always allow on all sessions" for a prompt that starts on "Allow once". The page has eight rows fewer, and the Models category is gone. The code and the keys ofconfig.tomlstay. A value that an earlier version saved is still read, anddefault_selected_permission = "reject"inconfig.tomlstill selects "Reject" in the first prompt of a session. Together with the change above, this ends the known issue of v0.7.0, "Some flags and settings have no effect", for everything that was measured. -
The
workflowsandbox on macOS has no kernel limit on memory or on the number of processes (docs, tests): measured in CI onmacos-15andmacos-15-intel. The layers that work there areprocess,rlimit-core,rlimit-cpu,rlimit-nofile,rlimit-fsizeand the audit hook.rlimit-ashas no effect (the system does not enforceRLIMIT_AS: a 1 GiB allocation succeeds under a 256 MiB limit), andrlimit-nprochas no effect (a script can start child processes). So only the audit hook stands in the way there. Before this, the design said that macOS was not tested.README.md,AGENTS.md,packages/pi-dynamic-workflows/README.mdanddocs/specs/2026-10-01-workflow-sandbox-design.mdsay it now, and the sandbox tests check what each platform has. No code of the sandbox changes. With the default permission modeask, everyworkflowcall is asked about first. -
The release workflow builds the macOS arm64 binary on
macos-15(CI): GitHub removes themacos-14runner on 2026-11-02. Only theruns-onofbuild-tui-macos-armchanges. Not run: this job runs only for a tag, so the first run is the release of 0.8.0. (#69) -
The TUI CI runs the tests of
pi-sandbox, with enforcement required (CI): the new suite is inscripts/tui_baseline.toml(105 passed, 3 skipped). The job installsbubblewrap, lifts the AppArmor block on unprivileged user namespaces ofubuntu-24.04, and setsSANDBOX_E2E_REQUIRE_ENFORCEMENT=1. A runner without Landlock or a workingbwrapnow fails the job; before, the tests returned early and counted as passed. (#59) -
New version numbers (all Python packages, TUI):
pi-agent-core-lc,pi-agent-harness-lcandpi-agent-cli-lcgo from 0.7.0 to 0.8.0. The three pin each other with==, so install them at one version. The four extension packages need the new core (pi-agent-core-lc>=0.8.0) and get new versions, because the publish step skips a version that PyPI has already:pi-web-access-py,pi-goal-x-pyandpi-dynamic-workflows-pygo from 0.4.0 to 0.5.0, andpi-mcp-pyfrom 0.2.0 to 0.3.0.zypi --versionand theagentInfoof the agent show 0.8.0 (the TUI cratespi-pager-binandpi-version, andtui/Cargo.lock, carry the same number).
Removed
- More dead code in the TUI: 16 unused modules, the MCP loaders, and the folder-trust gate that guarded them (TUI): the call graph (
scripts/tui_callgraph.py units) listed 16 modules that no live code refers to, and plan decision 10 named the MCP loaders ofpi-shellandrecent_dirsofpi-pager. All of it is gone. The modules are: inpi-hooks,dispatcher,result,runner,runner::commandandrunner::http(the hook dispatcher and the hook runners, 4,339 lines, and the crate's own integration test); inpi-shell,session::events,session::telemetry,session::unified_list,upload,upload::turn,auth::flowandconfig::reloader; andpi-workspace::publish,pi-tools::implementations::editor_infra,pi-mcp::wireandpi-agent::prompt::browser_verification. The compiler then found the code that only these called, and that is gone too: the plugin-registry and session-registry helpers ofpi-shell, the consume side of the folder-trust gate (the cache of verdicts andproject_scope_allowed; nothing calls the gate, because the loaders that it guarded are gone), the resolver of the[plugins]config, and the module...
pi-python v0.7.0
This release is about the first run. pi-agent-cli-lc now has a console script with the name of the package, so uvx pi-agent-cli-lc works, and a user who has no model and no key can set both with python -m pi_agent_cli --setup. An ACP client that can run a terminal login (Zed) gets the same dialog as a login. Nobody has run that login inside Zed yet. Fixed has the benchmark runner that showed the verifier's tests to the agent, -p that exited 0 after a failed model call, and the extension API defects of audit batch 9. Changed has the new version numbers: the extension packages need the new core. Known issues says what does not work yet.
Added
- A console script with the name of the package (
pi-agent-cli):pi-agent-cli-lcstarts the same function aspi-agent-cli, which stays.uvx <package>runs the executable that has the name of the package, and it stops with an error when the package has none. The package had onlypi-agent-cli, souvx pi-agent-cli-lc==0.6.0printed "An executable namedpi-agent-cli-lcis not provided by packagepi-agent-cli-lc". The ACP registry starts a PyPI agent only in this form (uvx <package> <args>; it has no--from), so a registry entry needs this script. It is on PyPI from 0.7.0.uvx --from pi-agent-cli-lc pi-agent-cliworks with every release. Test:packages/pi-agent-cli/tests/test_console_scripts.py. - The README tells how to start the agent in Zed as a custom agent (docs): the section "In Zed (custom agent)" has the
agent_serversentry (uvx --from pi-agent-cli-lc), the place of the model and key settings, and the entry for a checkout. The command of the entry was run with the published 0.6.0 package: the agent answeredinitializein an empty home. It was not run inside Zed. --setup, and a terminal login for ACP clients (pi-agent-cli): a user who has no model and no key can now set both from a terminal.python -m pi_agent_cli --setupasks for the provider (OpenAI, Anthropic or DeepSeek), the model and the key. It writes a[model]table toagent.toml(a new file, or the end of a file that has no such table; the text that is there stays to the byte) and the lineNAME=keytolocal.env(mode 0600 on POSIX). The key is read without echo and is never printed, logged or put inagent.toml. It stops, before the first question, for a file that is not TOML, for a[model]table that sets no provider, and for a home that has only the oldconfig.toml. The agent also offers the dialog to the client as an authentication method (pi-setup, "Set up model and API key"). A client that announcesauth.terminal(Zed) gets a method of typeterminal. A client that announces only_meta["terminal-auth"](older clients, the registry validator) gets a method with the command in_meta. Other clients,zypiincluded, getauthMethods: []as before. While the agent has no usable model,session/newanswersauth_required(-32000) to a client that can run the login, and the agent readsagent.tomlandlocal.envagain atsession/new,session/loadandsession/resume.session/loadandsession/resumenever refuse. A key that--setupreplaces needs a new agent process (load_local_envnever overwrites a variable that is set). Not run inside Zed. A script that acts as the client ran a live agent process on Linux (initialize,session/new, the login command in a pseudo-terminal,session/new,authenticate). Design:docs/specs/2026-10-28-acp-terminal-auth-setup-design.md. Tests:packages/pi-agent-cli/tests/test_setup_command.py,test_acp_auth_methods.py.- The product site for zycode (
web/, docs): a static site athttps://zycode.dev, in English (/) and in Chinese (/zh/). It is not a Python package, and no file of a release contains it.web/README.mdhas the commands, the deploy, and the rules for what the site may claim (every claim has a source in the repository). The<head>of a page has the canonical link, thehreflanglinks, the Open Graph and Twitter tags, JSON-LD data (WebSiteandSoftwareSourceCode) and the icons;robots.txtandsitemap.xmlare generated. The new workflowweb-ci.ymlrunsnpm run checkandnpm run buildfor a PR that changesweb/. A merge tomainthat changesweb/deploys the site by itself (Cloudflare Workers Builds, not GitHub Actions). The rootREADME.mdlinks to the site.
Changed
-
The release workflow builds the Linux binary on
ubuntu-24.04, not onubuntu-latest(CI): GitHub moves the labelubuntu-latestto Ubuntu 26. A binary that is built there can need a newer glibc than the 2.39 of Ubuntu 24.04, and then a machine with Ubuntu 24.04 could not start it. The jobbuild-tuiinrelease.ymlnow names the image, asTUI CIdoes (ADR6) and asbuild-tui-macos-armdoes withmacos-14. Nothing changes for the files of a release: the build ofv0.6.0ran onubuntu-24.04already. The other jobs ofrelease.yml(tests, Python packages, publishing) still useubuntu-latest; they ship no binary. -
The documentation says that the Linux binary needs glibc 2.39 or newer (docs):
docs/TUI-AND-CODE-AGENT.md, Option A. The binary ofv0.6.0hasGLIBC_2.39as its highest versioned symbol, so a system with an older glibc cannot start it (Ubuntu 22.04 has 2.35). Option B (build from source) works there. -
The workflows use the Node 24 versions of three GitHub Actions (CI):
actions/upload-artifactv4 to v6,actions/download-artifactv4 to v7, andactions/cachev4 to v5. GitHub removed Node 20 from its runners on 2026-09-23. It runs the old versions on Node 24 and prints a warning. The new versions change only the runtime; the inputs that the workflows use are the same.arduino/setup-protoc@v3stays: it has no release for Node 24, and it works on Node 24 (the runs of 2026-10-26 passed).download-artifactruns only in the jobs that publish, so a dry run ofrelease.ymldoes not test it. -
A change of the Python agent alone runs one test target of the pager, not the whole TUI CI (CI):
tui-ci.ymlstarted forpackages/pi-agent-cli/pi_agent_cli/**andpackages/pi-agent-cli/tests/_tool_agent.py, because the end-to-end tests of the pager (python_agent_e2e) talk to the agent. But it runs ten suites and takes 18 to 35 minutes, and a change of the Python agent can change only that one target. The runs for the PR of--setuptook 25 to 35 minutes, and the first two failed on a Rust test that the PR does not touch (fixed in #47). Now these two paths start the new workflowtui-e2e.yml, which runs that target alone. It restores the cache of thetestjob oftui-ci.ymland never saves one.tui-ci.ymlruns fortui/**,scripts/tui_baseline.py,scripts/tui_baseline.tomland its own file, and it still includes the end-to-end tests. A change of both runs both workflows. Tests:scripts/tests/test_tui_baseline.pykeeps the two workflows in line (paths, command, environment, install lines, cache key). -
The cache of the TUI CI keeps its key when GitHub updates the runner image (CI):
Swatinem/rust-cacheputs the version of every Rust toolchain on the runner into the cache key. The image has its ownstable, and the image of 2026-10-25 changed it from 1.99.0 to 1.99.1. The key changed, no cache matched, and thetestjob built everything again: 35 minutes, 31 of them for the build (18 minutes with the cache). Now the jobs oftui-ci.ymlandtui-e2e.ymlthat install the pinned toolchain make it the default and uninstall the other toolchains before the cache step (the stepKeep only the pinned Rust toolchain). So the key depends on the pinned toolchain, on the files oftui/and on theCARGO_*andRUST_BACKTRACEvariables of the workflow, and not on the image. The first run onmainafter this change builds everything once and saves the cache (onlymainsaves).add-rust-environment-hash-key: falseis not a substitute: it also removes the hash ofCargo.lockfrom the key, so the key never changes and the cache is saved only once. Tests:scripts/tests/test_tui_baseline.pykeeps the step equal in every job and before the cache step, and keeps the hashed variables equal in the two workflows. -
The PyPI pages link to the site (packaging): the
[project.urls]of the sevenpyproject.tomlfiles haveWebsite = "https://zycode.dev", and the README ofpi-agent-cli-lcsays that the agent is the one of zycode. The link shows on PyPI from this release. -
New version numbers (all Python packages, TUI):
pi-agent-core-lc,pi-agent-harness-lcandpi-agent-cli-lcgo from 0.6.0 to 0.7.0. The three pin each other with==, so install them at one version.zypi --versionand theagentInfoof the agent show 0.7.0 (theCargo.tomlfiles ofpi-pager-binandpi-versionandtui/Cargo.lockcarry the version ofpi-agent-cli-lc, and a test checks it). The extension packages are released with the core, so all four now requirepi-agent-core-lc>=0.7.0, andpi-dynamic-workflows-pyalso requirespi-agent-harness-lc>=0.7.0.pi-web-access-py,pi-goal-x-pyandpi-dynamic-workflows-pygo from 0.3.0 to 0.4.0, andpi-mcp-pygoes from 0.1.0 to 0.2.0. Their behaviour has not changed since 0.6.0 (pi-dynamic-workflows-pyhas a new comment inruntime.py); the new requirement and theWebsitelink are the changes. The publish step skips a version that is on PyPI already, so a new requirement needs a new version number.
Fixed
- The first turn without a model named a missing package (
pi-agent-cli): with no[model]table the provider ismock. LangChain has no such provider, and in an install without thelangchainpackage the turn failed with "Provider 'mock' needs init_chat_model from the 'langchain' package: pip install langchain". That text hid the real cause. Now the turn fails with "No model is set. Run the agent with --setup, or add a [mode...
pi-python v0.6.0
This release makes zypi ask the Python agent for sessions, and it adds an optional package that connects MCP servers. Changed says how --continue, --resume and --session-id work now, and that a session in which nobody typed has no file. pi-mcp-py (Added) connects the MCP servers that an editor such as Zed passes to the agent; without it, the agent still has no MCP client. Fixed has the Zed thread that did not open again after a restart. Removed lists the TUI code that read session files, and other dead code. Known issues says what does not work yet. Changed also has the new version numbers: the extension packages need the new core.
Added
- Tools that find tests which share process-wide state, and a helper that keeps such a test apart (TUI tests):
scripts/tui_envspy/andin_own_process. All tests of one Rust test binary run as threads of one process, so they share the environment variables, the working directory and everystatic. The vendored tests were written forcargo nextest(one process per test), so their locks are partial, and a test that changes such state can break another test in some runs only.envspy.shrunscargo testwith anLD_PRELOADshim overgetenv,setenv,unsetenv,putenvandchdir, andanalyze.pylists the tests that change a variable while another test runs, leave it changed, or read what another test has changed.stress.shruns the lib tests of one package many times, each time in a random order (--shuffle-seed, so a failing run can be repeated) and with many test threads. It can also run a few named tests (a suspect pair, or a victim beside all tests of one module), and withENVSPY_TAGit runs the tests through the spy. Linux only. 10 newpytesttests cover the analyzer.crate::test_util::in_own_process(|| { … })inpi-pagerruns the body of a test in a child process (the same test binary,--exact, one test thread), and the test fails when the child fails; 57 tests use it, directly or throughwith_rtl_bidi,with_theme_test_envandwith_test_env, and 2 new tests check it. The README ofscripts/tui_envspy/has the rules for a test that changes process-wide state, and what the tools do not find. Numbers: section 3.4 ofdocs/baselines/tui.md. - Extensions can take over the MCP servers a client passes, tell the user something, and hold a turn back (
pi-agent-core,pi-agent-harness,pi-agent-cli): the agent still has no MCP client, and it still does not readmcp.json. What a client passes inmcp_serversnow goes to the harness as data:ClientMcpServer(name,transport,command,args,env,url,headers; itsreprshows onlynameandtransport, because the other fields can hold tokens),AgentHarness.set_client_mcp_servers()(before the extensions load), andExtensionAPI.client_mcp_serverswithclaim_client_mcp_servers(names). Three general entry points come with it.ExtensionAPI.notify(text)shows one line to the user (aNoticeEvent, which is an agent message in ACP; the model does not see it, the session does not save it, and the agent holds it until the client knows the session).ExtensionAPI.register_cleanup(callback)is the public name of what the bridge already had.ExtensionAPI.add_turn_barrier(wait)makes the harness awaitwaitbefore it builds the tool list of a turn;session/cancel, closing the session and opening it again end that wait in the CLI.initializesays that the agent takeshttpandsseMCP servers only when thepi-mcpextension entry point is installed. The extension that uses these entry points ispi-mcp-py(next entry). Design:docs/specs/2026-10-23-mcp-extension-design.md. pi-mcp-py: an optional extension that connects the MCP servers an ACP client passes (new packagepackages/pi-mcp, version 0.1.0): an editor such as Zed passes its MCP servers insession/new. With this package installed, the agent connects them (stdio, Streamable HTTP and SSE) and gives their tools to the model. The core, the harness and the CLI still have no MCP client, and the agent still reads nomcp.json; the extension takes the servers frompi.client_mcp_serversand claims them, so the notice "its tools are not available" does not appear. Without the package, nothing changes. Tools: each tool is namedmcp__<server>__<tool>(letters, digits,_and-; at most 64 characters, cut with a hash; a repeated name gets_2,_3). The prefix is needed: a tool of an extension that has the name of a built-in tool replaces it, and an MCP tool namedbashwould take overbash. The permission modeaskasks about every MCP tool call unless the server declares the tool harmless (readOnlyHint, or neither destructive nor open-world), as for all tools. A result is text; text over 50 KB is cut with a note, a result with onlystructuredContentis sent as JSON text, and an MCP error result becomes an error tool result. One task for each server: the MCP SDK usesanyiocancel scopes, which must end in the task that began them.session_startstarts the servers and returns at once; a turn waits at most 10 s for servers that are still starting (session/cancelends the wait) and then goes on without them. A server that is slower sends one notice, and its tools join in a later turn. Limits (environment variables):PI_MCP_STARTUP_TIMEOUT(60 s) andPI_MCP_CALL_TIMEOUT(120 s); a call also ends when the user cancels the turn. When a server stops: the user gets one notice for each server and kind of event (did not start, stopped, cannot reconnect), and a good start gets none, so a thread without MCP trouble stays blank. The next tool call connects the server again, at most 3 times in a row (a call that gets an answer starts the count again). A call that was already sent when the server died is not repeated, because the server may have run it./mcplists the servers (name, transport, state, number of tools), and/mcp reconnect <name>connects one again with no limit. Secrets:env,headers,argsandurlhold tokens. Notices,/mcp, errors and logs show only the server name, the name of the executable, or the host; the values ofenv, the values ofheadersand the URL are masked in error text. Environment of a stdio server: the default list of the MCP SDK, the proxy, certificate, locale and time zone variables, and theenvof the client.${VAR}is not expanded (langchain-mcp-adapterswould expand it from the environment of the agent, which has the LLM key, so the extension starts stdio servers itself), and the keys of the LLM provider are not passed. stderr of a server goes to<home>/logs/mcp/<name>.log, not to the stderr of the agent, where a full pipe would stop it. Servers on this machine are reached without the proxy of the system:httpxreads the proxy settings of Windows and does not skip loopback addresses. End: when the session closes, each server gets its stdin closed first, and then its process tree is stopped; a test checks that no process stays, on Windows and on Linux. Not supported: OAuth (it works only if the editor passes a token inheaders; not verified), MCP resources, prompts, sampling and elicitation, a server shared by two sessions (each session starts its own), and a configuration file. A legacy SSE server whose POST fails ends a call only with the call timeout (the SDK hides the error). Limits of the package: it needspi-agent-core-lc0.6.0 or later; an older core makes it log a warning and do nothing. TheReleaseworkflow installs the package for its pre-release tests and builds it with the other packages, so the upload to PyPI has it (pip install pi-mcp-py). Tests: 192 inpackages/pi-mcp/testswith real child processes (stdio, HTTP, SSE), the realAgentHarness, and the realPiAcpAgentover ACP, run on Windows and WSL. Design:docs/specs/2026-10-23-mcp-extension-design.md.
Changed
zypi --continue,--resumeand--session-idask the agent which session is meant (TUI):zypiused to look for the session in the session files of the old Rust agent. The Python agent writes another layout, so--resume <title>found no session, an unknown id failed only after the UI had started, a used--session-idleft the UI open withSession creation failed: Invalid params, and--continuetook the session that was created last (the first of the Known issues of 0.5.0). Nowzypistarts the agent first and then asks it over ACP, withsession/list(page after page) andsession/load. It reads no session file for this. A session that does not exist ends the run before the event loop starts: the terminal is restored, the agent is stopped, the message is printed, and the exit status is 1. Behaviour changes:--continueand--resumewithout a value take the session of this directory with the latest activity (the time of its last write), not the one that was created last.--resume <id>finds the id in every directory and loads the session in the directory it belongs to.--resume <title>matches the title, which is the first message of the session, among the sessions of this directory, without regard to letter case and to spaces at the ends; two sessions with the same title are an error that lists their ids and last activity.--session-id <id>reads every page of the session list and refuses an id that any session has (Session ID <id> is already in use., exit status 1); that costs one pass through the list (scripts/session_list_bench.pyprints it aswalk all), and an agent that offers nosession/listskips the check and refuses a used id itself. The lookup stops after 30 s, the limit of the connect (GROK_CONNECT_UI_TIMEOUT_SECS). A session in which nobody typed has no file now, so--continuecannot take it (see the next entry). Not solved, and left as it is: the agent lists its session directory a...
pi-python v0.5.0
zypi no longer has an agent of its own. It draws the terminal screen and talks ACP to the Python agent (python -m pi_agent_cli), and that agent is now the only agent. Removed says what went with the Rust agent runtime. Known issues says what does not work yet. This release also has the Phase 6 and Phase 7 changes to the Python packages: project trust, a sandbox for workflow scripts, DeepSeek thinking with tools, and tool annotations.
Added
- Project trust (Phase 7): a project's own extensions, saved workflows (
.pi-python/workflows),.pi/SYSTEM.md,.pi/APPEND_SYSTEM.mdand project-relative[skills].pathsare now used only once the user has vouched for the project; before, opening a repository imported its extensions and turned its saved workflows into slash commands.[extensions] default_project_trust = "ask"(the default; or"never"/"always") has the ACP client answer asession/request_permission(Don't trust/Trust and remember; noallow_once, so a client that auto-approves tool calls cannot answer it) before anything of the project loads. The answer is saved in~/.pi-python/agent/trust.jsonwith asha256:fingerprint of the files, so agit pullthat changes them asks again.trusted_projects,trust_project_extensions = true,--trust-project-extensionsandPI_TRUST_PROJECT_EXTENSIONS=1also trust; headless (-p) runs never ask but honour a saved answer that still matches. Extensions read the answer aspi.project_trusted; library users passAgentHarness(trust_project_extensions=True). Behaviour change: ACP sessions in such projects now ask (default_project_trust = "never"keeps the old silent skip). Not covered: a client may auto-approve the request, files changed after the session started are not re-checked, andAGENTS.md/CLAUDE.mdare not gated (as in upstream pi). Design: Phase 7 spec, sections 4.4 and 16. - Workflow scripts run in a process of their own, under limits the operating system enforces (Phase 7): scripts used to be
exec'd in the host's interpreter, where the narrowed__builtins__had two known ways out and awhile Truefroze the host's event loop. EachWorkflowRuntime.execute()now starts a child process with an empty environment; the host does everything with an effect (sub-agents, journal, logs, budget) over a JSON-lines pipe and treats the child as hostile. The kernel limits the child (rlimits on POSIX, plusno_new_privsand noforkon Linux; a Job Object and a lowered integrity level on Windows), andWorkflowRuntime(sandbox_limits=SandboxLimits(...))sets them (an optional wall-clock deadline among them). What this is not: the audit hook is a speed bump, and what the kernel still allows differs per platform (a script can still read the files the user can read and use the network), so run the agent in a container if scripts cannot be trusted. Behaviour changes: values cross the process boundary as JSON,budgetin a script is read-only, a run costs a process start (about 0.06 s on Linux, 0.14 s on Windows), Windows needs the default event loop, andexecute()raisesWorkflowScriptErrorfor a script that raises and aSandboxErrorfor one that is stopped. Design:docs/specs/2026-10-01-workflow-sandbox-design.md. - DeepSeek thinking works together with tools (Phase 6): with thinking on, DeepSeek's own API answers 400 to a request with tools unless earlier assistant messages bring their
reasoning_contentback, whichChatDeepSeekleaves out. The adapter now keeps it and sends it back on every assistant message (adapters/deepseek_replay.py), and it states whether to think (thinking: enabled | disabled,reasoning_effort) instead of leaving it to the API's default (on). Both apply to DeepSeek's own API only (nobase_url, or one ondeepseek.com), not to gateways serving DeepSeek models. Behaviour change: with the defaultthinking_level="off", requests now sendthinking: disabled; code that relied on the old default to get reasoning blocks must setModel.reasoning=Trueand athinking_level. Not verified against the live API (no key). Spec: Phase 6, section 5.5. [model] reasoninginagent.toml(Phase 6): whether the configured model can reason (Model.reasoning); unset, anythinking_levelother thanoffsays it can. This is what makesthinking_leveltake effect through the CLI, which built every model withreasoning=False(the level was dropped on OpenAI and Anthropic). A model that rejects reasoning parameters needsreasoning = false.- Tools can declare what they do:
ToolAnnotations(Phase 7): MCP's four hints (readOnlyHint,destructiveHint,idempotentHint,openWorldHint; a tool that declares nothing claims nothing).SimpleTool,CodingToolandToolDefinitiontakeannotations=,from_langchain_toolreads them fromtool.metadata, andtool_callhooks receive them asToolCallEvent.annotations. Built in,read,grep,find,ls,web_searchandfetch_urldeclare themselves read-only,goal_updateandgoal_completeneither destructive nor open-world, andbash,edit,writeandworkflowdeclare nothing, on purpose. The hints are the tool author's word and are not verified;askmode uses them (see Changed). Design: Phase 7 spec, section 12. - The TUI keeps the agent's stderr:
zypipoints its own stderr at/dev/nulland the agent inherited it, so a Python traceback or warning from the agent went nowhere. It is now appended to<home>/logs/agent.stderr.log(mode 0600 on Unix, one--- agent started ... ---line per run, moved toagent.stderr.log.1once it is over 1 MiB at the next start).zypi -pis unchanged: the agent's stderr stays on the terminal. - A sandbox that cannot be put in force says so (TUI): on a kernel without Landlock (Linux before 5.13, or with it disabled)
zypi --sandbox <profile>used to start without the file and network limits and without a word; the only trace was anApplyFailedline insandbox-events.jsonl. It now prints a warning on stderr at start-up and showssandbox:<profile> (not enforced)in the welcome page's top bar and the status bar, where the label of an applied profile is drawn. Where the sandbox is applied nothing changes. session/listcomes in pages (pi-agent-cli,pi-agent-harness): the agent answers with at most 50 sessions, newest first, and anextCursorwhile more follow; the cursor is opaque, carries the position in it (the agent keeps nothing between requests, so it survives a restart) and anything the agent did not issue isinvalid_params. The TUI keeps asking until the cursor is gone, so its session picker still lists everything. For library usersJsonlSessionRepo.list_page(...)returns one page andJsonlSessionRepo.find(id)reads one session's header instead of the directory. With 1,000 sessions of 120 entries on Windows, the first answer took 2.0 s and asession/loadof one of them 2.0 s; reading a file's first line no longer reads the file and a lookup by id no longer lists the directory, which makes them 0.11 s and 0.12 s (scripts/session_list_bench.py; numbers and method indocs/PLAN/archive/PLAN-RUST-AGENT-RUNTIME-REMOVAL-EXECUTION-LOG.md§10.11).updated_atis still the file's mtime and need not follow the list's order, which is by creation time.- The agent says which MCP servers a session ignored (
pi-agent-cli): the agent connects to no MCP server (a Phase 4 non-goal), which departs from ACP's "an agent MUST support stdio servers";session/new,loadandresumeused to drop the list without a word. Each session set up with a non-empty list now logs a warning and sends one agent message naming the servers (names only:env,headers,argsandurlhold tokens).zypino longer forwards what it finds in.mcp.json,~/.claude.jsonand Cursor'smcp.json, so there the message never shows; it is for other ACP clients, an editor that passes its own servers, say. - A client can choose the id of a new session, and the agent says which directory a session works in (
pi-agent-cli,pi-agent-harness):zypi --session-id <uuid>has always sent the id insession/new's_meta.sessionId; the agent ignored it and picked its own, so the flag did nothing on the Python agent. It now becomes the session's id. It must be a UUID in any formuuid.UUIDreads (kept as lower case with hyphens); anything else isinvalid_params, because the id ends up in a file name. An id some session already has is refused withinvalid_params, whatever directory that session belongs to, and two requests for the same id at once make one session.session/new,loadandresumeanswer with_meta["pi/cwd"], the directory the session works in; forloadandresumethe one saved with the session wins over thecwdthe client sent. For library usersJsonlSessionRepo.has_session(id)says whether an id is taken and opens only the files whose names do not already say they belong to another session (findreads every header for an id nobody has). A free id costs the directory listing: 0.07 s at 1,000 sessions, 0.3 s at 5,000, on Windows and WSL2 alike (scripts/session_list_bench.py). Not yet in the TUI:zypistill checks for a used id in the old session layout, so it never sees the Python agent's sessions; the agent refuses the id and the TUI showsSession creation failed: Invalid params: {...}instead of exiting 1 with a message (scripts/tui_pty/resume_matrix.py, case 11), and nothing readspi/cwdyet (execution log section 10.11, ADR1 PR 2). - A call graph of the TUI workspace (
scripts/tui_callgraph.py): it reads the Rust source as text, so it needs nocargoand no network, and it gives the same answer on Windows, Linux and macOS.cratesshows the dependency closure of the product crates (78 crates in the workspace, 74 in the closure;--without pi-shellshows what only `...
pi-python v0.4.0
Added
- Phase 6 (git context):
pi_agent_cliinjects a bounded, read-only<git_status>block into the system prompt on every turn. Disable with[git] enabled = falseor headless--no-git-context. Non-repos, timeouts, and git failures omit the section. MCP stays out of the product path; callers still adaptlangchain-mcp-adapterstools withfrom_langchain_tool(). - Provider matrix: opt-in live tests for OpenAI, Anthropic, DeepSeek, and SiliconFlow (
pytest -m real_llm pi_agent_core/tests/test_provider_matrix.py). Manual workflow.github/workflows/provider-matrix.yml(workflow_dispatchonly; missing secrets skip). - Phase 7 extension packages are built and published with the release:
pi-web-access-py,pi-goal-x-py,pi-dynamic-workflows-py. CI and the pre-release test job install all three.
Changed
pi-agent-core-lc,pi-agent-harness-lc, andpi-agent-cli-lcare version0.4.0.
pi-python v0.3.0
Added
- TUI binary in GitHub Releases (beta): prebuilt
zypiLinux x86_64 binary is now included in GitHub Releases alongside Python wheels. Downloadzypi-linux-x86_64from the release assets page. Build from source for other platforms. - Phase 5 (Coding Agent prompt engine): pi-aligned
build_system_prompt()/build_coding_agent_harness_system_prompt()inpi_agent_cli; toolprompt_snippet/prompt_guidelinescontributions;AGENTS.md/CLAUDE.md/SYSTEM.md/APPEND_SYSTEM.mdcontext files;<available_skills>XML format; bashPI_*env. - FrontierHarness 30-task evaluation:
scripts/run_eval.pyCLI with 21 Terminal-Bench + 9 DeepSWE industrial tasks; dual runtime (Windows local NTFS junctions + WSL2 Docker sandbox); v2ray proxy integration; full report indocs/benchmarks/FRONTIER-HARNESS-30-EVAL-REPORT.md. - TUI & Code-Agent guide: new
docs/TUI-AND-CODE-AGENT.mdcovering installation, configuration, and usage for bothzypiTUI andpi_agent_cli. - Release workflow:
build-tuijob builds the Rust TUI binary (cargo build --profile release-dist -p pi-pager-bin) and uploadszypi-linux-x86_64to GitHub Releases.
Changed
- TUI
x.ai/residual cleanup (P4P5-1 resolution): systematic purge of 680x.ai/references across 115 files intui/crates/codegen/pi-pager/src/. Newacp/vendor.rscentralises vendor-prefix constants and helpers; 4 inbound filter sites usevendor::is_vendor_ext_method()as defensive guards; doc comments, test fixtures, views/dispatch literals all scrubbed. Onlyvendor.rs(4 constants) andworktree_cmd/mod.rs(20, deferred) retainx.ai/strings. - Test regression fixes: fixed 5 x.ai cleanup regressions where test data was over-renamed to
pi/while handler code still used original constant names (TITLE_IS_MANUAL_META_KEY,PI_SESSION_UPDATE_METHOD,is_vendor_meta_key). - Grok-specific tests marked
#[ignore]: 62 tests for features removed from pi-python (grok slash commands,/loopscheduler,/share, dashboard,~/.grokpath,agentsubcommand) annotated with#[ignore = "pi-python: grok-specific feature not supported"]. - TUI test result:
8880 passed, 0 failed, 72 ignored. - TUI and code-agent marked as beta in README.
- README restructured: added Status table, TUI install section, and updated roadmap.
pi-python v0.2.0
Added
- Phase 4 P0–P4 (Coding Agent CLI): vendored grok-build TUI under
tui/(Apache-2.0, excluded from wheels);packages/pi-agent-clistandard ACP agent overAgentHarness(nox.ai/*); TUI spawnpython -m pi_agent_cli, skip xAI login, drop vendor extension RPCs, home~/.pi-python, product binaryzypi. TUI Cargo crates renamedxai-*→pi-*, then P5 de-grok:pi-grok-*→pi-*, grok CLI subcommands removed, auth/prefetch startup skipped whenauth_methodsis empty./new→session/new,/resume→session/list+session/load;@is in-process directory listing;zypi -p/python -m pi_agent_cli -pis Python headless. P4:config.toml(model, permission, skills,[agent].command), coding system prompt + skills XML, Windows notes indocs/WINDOWS.md. - Pelican-on-a-bicycle foundation benchmark:
pi_agent_cli.benchmarks.pelican,scripts/smoke_pelican.py,docs/benchmarks/PELCAN-BICYCLE.md— structural SVG smoke test for the pi TUI agent path. - PyPI:
pi-agent-cli-lcincluded in the release workflow (tagv*builds and publishes core, harness, and cli wheels/sdists; Rust TUI excluded).
pi-python v0.1.0
Initial public release.
Added
pi-agent-core
- Core runtime (Phase 1):
Agentwith steering/follow-up queues, abort,subscribe()event barrier;agent_loopwith pi-compatible event protocol, parallel/sequential tool execution,terminatesemantics. - LangChain adapter:
StreamFnoverastream()for OpenAI / Anthropic / DeepSeek / anyinit_chat_modelprovider; mock stream for tests (no API keys needed). - Production hardening (Phase 2/2.5):
- Cross-provider message replay (
transform_messages: tool-call id normalization, thinking downgrade, image stripping). - Usage & cost tracking with
CostCalculator. - Thinking/reasoning: provider param mapping, streamed
thinking_deltaevents, Anthropic signature replay, DeepSeek-stylereasoning_content. - Stream-level retries with exponential backoff + jitter,
Retry-Afteraware. - Runaway protection:
max_turns/tool_timeout. - Guardrail hooks:
before_llm_call(withContextBudget),after_llm_call,on_agent_end. - Observability:
on_payload/on_responsehooks;run_id/turn_idon every event. - Granular stream events:
text_start/end,thinking_start/end,toolcall_start/end. - Structured output:
response_schema(Pydantic model or JSON schema). - Tool-result images: Anthropic native blocks, user-message fallback elsewhere.
- OpenAI-compatible gateways via
Model.base_url.
- Cross-provider message replay (
- Tool ecosystem (P6):
- 7 built-in coding tools:
read,bash,edit,write,grep,find,ls. - Group factories:
create_coding_tools()/create_read_only_tools(). - LangChain
BaseTool→AgentTooladapter (from_langchain_tool).
- 7 built-in coding tools:
pi-agent-harness
- Session tree (H1):
SessionRepowith filesystem storage,uuid7IDs, branch/fork semantics, message append/list. - AgentHarness runtime (H2): orchestration layer over
Agent, queue rollback, abort aggregation, event-based idle wait. - Compaction & tree navigation (H3): token-based compaction with branch summaries,
navigate_treefor branch exploration. - Skills, templates & env (H4): skill discovery, prompt templates, system prompt injection,
LocalExecutionEnvfor sandboxed execution.