Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 18:56
Immutable release. Only release title and notes can be modified.
7602263
040-herd

PiG 0.4.0 is a Go implementation of Pi 1.0.0. It collects the changes since PiG 0.3.1, which followed Pi 0.87.1: the upstream 0.99 releases (MCP, codemode, virtual models), Pi 1.0.0's leaner codemode, MCP OAuth hardening, fullscreen by default and new sign-in options, and PiG's own header, /sprite with fifteen sprites, and easter eggs. Upgrade with pig update, or npm update -g @pi-in-go/pig for an npm installation; on 0.3.0 installed with npm, use only npm update (see Updating).

Thanks to everyone who reported an issue or sent a change, in this release and in 0.3.1. Details are under Thanks. Extension and SDK authors: this release breaks the Rust SDK and some Go library signatures to match Pi. The Go and Python extension SDKs only gain API. See Breaking changes.

Pi is the reference implementation: earendil-works/pi (documentation). PiG is a separate Go implementation of it, created by Michael Kinsy and originally developed at Hewlett Packard Enterprise. The differences a user can see are recorded, numbered, in the divergence ledger.

Updating

  • Installed with the script or a standalone download, on 0.3.0 or 0.3.1: run pig update. It checks the signed update.json, downloads the archive for your platform and verifies its SHA-256 before it replaces the executable. The first start after the update rebuilds source extensions, so it is slower.
  • Installed with npm, on 0.3.1: run pig update or npm update -g @pi-in-go/pig. On 0.3.0, do not use pig update for this one step: 0.3.0 reads the wrong package name from the update manifest and runs npm install -g pig@<version>, which installs an unrelated package. Run npm update -g @pi-in-go/pig.
  • On 0.2.0: run the installer again. PiG 0.2.0 cannot update a script installation itself. On macOS and Linux: curl -fsSL https://pi-in-go.dev/install.sh | sh. On Windows: irm https://pi-in-go.dev/install.ps1 | iex.
  • On Windows with a standalone pig.exe: pig update does not replace a running pig.exe in place (D39). Run the PowerShell installer again.
  • Installed with Go: run go install github.com/MichaelKinsy/PiG/cmd/pig@v0.4.0 again.
  • Stop running PiG processes before you update.

Added

  • Codemode and tool search. The codemode tool runs model-written JavaScript in a QuickJS sandbox that calls PiG's tools, and tool_search finds tools that were not declared to the model. Both are built-in extensions written in Go; the JavaScript engine is QuickJS compiled to WebAssembly and run by wazero, so codemode needs no Node. Enable codemode with defaultTools or --tools, for example "defaultTools": ["+codemode"], and configure it with the codemode.mode and codemode.inlineBudget settings. See pig docs show codemode.

  • Leaner codemode, as in Pi 1.0.0. The codemode description lists the script globals in one line each and points to the codemode page of the docs bundle for the models API. Declared tools say in one line how scripts call them and what the call resolves to, instead of repeating their declaration, and the system prompt's codemode guidance and MCP server section are shorter. Errors say how to recover: reading a tool or models member that does not exist names the close matches (tools.Bash suggests tools.bash), malformed models.classify() and models.generateImages() arguments report the expected shape, an unknown model points to models.getAvailableOfType(), and an oversized store() value explains what the store is for. Scripts that probed for a tool with typeof tools.name must use "name" in tools.

  • Image generation in codemode. Scripts call models.generateImages(model, { input }). It runs image models such as OpenRouter's with the session's credentials and returns base64 image blocks that image() attaches to the result. Usage counts toward the session cost like models.classify(). Extensions call ctx.modelRegistry.generateImages() in every SDK.

  • Model Context Protocol client, in every mode. PiG has a native MCP client in Go with stdio and streamable HTTP transports and OAuth (discovery, PKCE, dynamic client registration, refresh). The built-in mcp extension loads mcp.json (global, or .pig/mcp.json in a trusted project) in print, JSON, RPC and interactive mode, registers mcp__<server>__<tool> tools plus resource tools, adds the /mcp command, and keeps OAuth credentials in mcp-auth.json. pig mcp add, remove, list, login and logout manage servers without starting a session. Servers with the default codemode exposure connect in the background and do not delay the first prompt. pig mcp add takes --description and --oauth-client-name, and an HTTP server can use "auth": {"provider": "<provider>"} to send a provider's current /login token as the bearer token. Disable the extension with --no-extensions or -builtin:mcp. See pig docs show mcp.

  • MCP OAuth hardening, as in Pi 1.0.0. oauth.authServerMetadataUrl names the authorization server metadata document to use instead of discovery, for servers that advertise a wrong authorization server or none (https, or localhost, 127.0.0.1 or [::1]). Sign-in rejects an authorization response whose iss parameter names another authorization server before it exchanges the code (RFC 9207). Credentials are stored per server name and URL, so two servers with one URL can sign in with different accounts, and credentials stored by URL alone move to the first server that uses them. A server that asks for more scope (insufficient_scope) gets a sign-in that keeps the scope granted so far. /mcp login prints the sign-in URL as a terminal link that stays clickable when it wraps.

  • Virtual models. An extension registers a virtual model and chooses the physical model and thinking level for each request. The footer shows the routed model, and retries, compaction summaries and restored selections follow the route. See pig docs show virtual-models.

  • pi-durable in Go. Go packages port Pi 1.0.0's @earendil-works/pi-durable with its upstream tests: durable (types, IDs, errors, entries, documents, tasks, truncation), durable/storage (memory, JSONL and SQLite storage; SQLite runs on the pure-Go modernc.org/sqlite, so CGO_ENABLED=0 builds keep it), durable/session (the Session kernel: transactions, document trackers, forks, document states and watches), durable/harness (agents, conversations, generation, scheduling, compaction, tasks and tools), durable/tools (read, write, edit and bash over a durable environment) and durable/env, with chord/delta, the Chord overlay draft tracker. The experimental services run on the durable harness in builds with the pig_experimental tag; the released pig binary does not include them.

  • pi-telemetry in Go. The telemetry package ports Pi 1.0.0's telemetry schema data types, DefineTelemetrySchema, CreateTypedSpanStarter and the in-memory telemetry context, and telemetry/telemetrytest holds the runner-independent conformance cases for telemetry adapters.

  • Typed models, image generation and classifiers. coding.ModelRuntime lists and resolves chat, image and classifier models, generates images with the provider's key and headers (GenerateImages), and keeps OpenRouter image models apart from chat models. llama.cpp lists a classifier next to each chat model.

  • The extension API for tools, MCP and models, in every SDK. Node, Go, Python and Rust extensions can register MCP servers and virtual models, read settings (getSettings), declare tools with exposure (direct, codemode, deferred, hidden), namespace, annotations, outputSchema, defaultActive and prepareLoadout, return structuredContent, call other tools with ctx.tools and ctx.executeTool(), observe provider_stream_event, and read ctx.signal, the signal of the run in progress, as Pi does. Go, Python and Rust extensions also get Pi's pi.events bus. The TypeScript declarations are Pi 1.0.0's.

  • Sign in with ChatGPT. /login openai uses a ChatGPT subscription with the OpenAI provider. GPT-6.1 Sol is in the catalog and is the default openai-codex model; Fireworks, Together and OpenCode Go default to Kimi K3.

  • Anthropic workload identity federation. PiG authenticates to Anthropic from ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID and ANTHROPIC_IDENTITY_TOKEN_FILE.

  • System theme. PiG's colors come from your terminal's palette by default (the system theme) and follow a light/dark switch. Theme files accept #rgb, oklch() and okhsl() colors. The theme keeps the palette's chroma, so pastel palettes such as Catppuccin Frappe are no longer made more vivid.

  • Sign in with Radius. /login offers Radius as the last top-level option, with its status. After a Radius sign-in, /login offers to add the Radius MCP server to the global mcp.json with "auth": {"provider": "radius"} and reloads.

  • Anthropic copy code login. /login for Anthropic asks for browser login or copy code login. Copy code login prints a URL to open on any machine and takes the code Anthropic shows, so it works when the browser runs on another machine.

  • Fullscreen by default. Interactive mode starts in fullscreen. Set tuiMode to "regular", or pass --tui-mode regular, to keep the terminal's normal scrollback. quietStartup accepts "header", which keeps the startup header and hides the loaded-resource listing (true hides both, false shows both; /settings offers the three values).

  • The PiG header and /sprite. The startup header shows the sprite's pixel pig where it showed Pi's logo: seven lines tall, with the version, the key hints and the onboarding line beside it. 256-color terminals and panes too narrow for the head get a one-line PiG. mark, and Apple Terminal gets Pi's Apple Terminal layout with that mark. /sprite chooses the pig from fifteen sprites (/sprite list, /sprite set <id>, or a picker): the default green pig, eight colors, and the characters Pigrogu, Darth Vader, Kratos, Piglet, Spider-Ham and Sheriff PiG. /sprite preview [id] shows a sprite's full art with the PiG. wordmark. The choice is saved in $PIG_HOME/state/pig-standard/login.json. /sprite comes from the built-in pig-login extension, which pig config lists with the other built-in extensions. Extensions and Piglets add sprites with ctx.ui.registerSprite (Go RegisterSprite, Rust and Python register_sprite). An extension can still replace the header with setHeader.

  • Easter eggs. In fullscreen mode, click the pig head in the startup header (or the PiG. mark): the screen dissolves into braille dust, and the pig head flies to the center, grows and spins, then turns into a big side-view pig, drawn in the same braille dots, that runs in place. Escape or Ctrl+C lands it back on the header (press again to skip). It is PiG's version of Pi 1.0.0's logo animation, in the colors of your /sprite. /pigsayhi is a second name for /arminsayshi, which now draws a pig head labeled pigsayhi with Pi's effects (D87).

  • Games over MCP, demonstrated. The 0.4.0 demo is one real terminal take: first-run setup picks the Sheriff sprite, then Jev, the typesafe/jev-latest classifier, plays Pig Runner and Angry Pigs live in their terminal overlays through code mode over MCP, one models.classify call per move, and a click on the header shows the braille running pig. It is on the Demos page.

  • Built-in extensions in pig config. pig config lists built-in extensions in a "Built-in" group. Enable or disable one with +builtin:<name> or -builtin:<name> in the extensions setting, load one for a run with -e builtin:<name>, and disable all of them with --no-extensions.

  • /reload picks up new default tools. Tools newly added to defaultTools are enabled on /reload. defaultTools also accepts +name and -name.

  • Sessions are saved from the first prompt. The session file is created when the first user message is added, so a crash before the first reply no longer loses the prompt.

  • More settings. fullscreenWheelScrollLines ("auto" or 1 to 100) and a random installation deviceId in the global settings, which bug reports leave out.

  • Termux on Android (arm64). curl -fsSL https://pi-in-go.dev/install.sh | sh detects Termux and installs the new android-arm64 release into $PREFIX/bin; npm install -g @pi-in-go/pig installs the new @pi-in-go/pig-android-arm64 package. The Android binary is a cgo build against Android's libc, started through linker64; PiG sets TMPDIR and SSL_CERT_FILE for Termux when they are unset (#119).

  • pig piglet build without a checkout. A release binary fetches the PiG source of exactly its own version through the Go module proxy (checked by GOSUMDB, cached after the first build). A built-in container builder runs the build in Podman or Docker when Go is missing or for other linux/<arch> targets.

Changed

  • Sign-in labels. /login and /logout label providers without credentials "not configured" instead of "unconfigured". Only subscription-backed OAuth sign-ins say "subscription"; the others, Radius and OpenRouter among them, say "account". Cancelling a sign-in method prompt returns to the menu the login started from. Login dialogs show each flow's own paste prompt. OAuth browser pages show the color logo.
  • --provider needs --model. --provider without --model fails with an error instead of running the default model of another provider.
  • Apple Terminal header. The header shows the one-line PiG. mark and the version on the first line and the key hints below.
  • Syntax highlighting as in Pi. PiG highlights code with a Go port of highlight.js 10.7.3, the highlighter Pi uses, instead of chroma. Code blocks in messages, tool output and codemode cards get Pi's token colors for every language, and pig starts faster. As in Pi, the 20 most common languages highlight at startup and the rest load after the first screen, which then repaints. Tool previews take the language from the text after the last dot of the path, so a Makefile or Dockerfile inside a directory is no longer highlighted.

Fixed

  • Extension widgets and footers stay visible in fullscreen after a terminal resize and re-render at the new width, and belowEditor widgets sit between the editor and the footer in both TUI modes. Thanks @baggiiiie (#121, #122, shipped through #114).
  • A Piglet whose root tools names an extension tool gets an error that points to extensions[].tools; a package that lists both a directory and its own index file resolves to one extension; extension tools with Pi source info are no longer scoped as built-ins.
  • On Windows, pig update for an npm installation finds its running executable when PiG was started through an 8.3 short path.
  • An extension keeps the terminal height after /reload. A packed Go extension received a height of 0 after a reload until the next resize. The host now delivers width and height changes to each extension that has not seen the value. Thanks @nicholas-recht (#115, #116).
  • On Windows, publishing a Node extension cell (and any other cached runtime cell) is retried with backoff for up to 10 seconds when the final directory rename fails with Access is denied, a sharing violation or a lock violation, instead of stopping extension loading. Thanks @worldofgeese (#106, #110).
  • Fullscreen mode keeps the Kitty keyboard flags on the alternate screen, so Shift+Enter and ctrl+digit extension shortcuts work there, and PiG no longer leaves the flags set in the shell after it exits fullscreen. The bash execution block, the compaction and branch summary label, the editor's thinking-level border and the settings list cursor take their colors from the active theme. Thanks @jkerdreux-imt (#105, #108, #109).
  • MCP stdio servers started through npm .cmd shims on Windows run through cmd.exe, so npx-style servers start.
  • On Windows, pasting a Print Screen or other bitmap-only clipboard image with Alt+V inserts the image, a Python extension no longer resolves Python to the Microsoft Store python.exe alias, and the PIG_DEBUG log is written below TEMP instead of the missing C:\tmp.
  • Parallel MCP tool calls reach the server in the order the model issued them.
  • Codemode image() rejects malformed base64 and unsupported image types instead of persisting an image block that makes every later provider request fail with HTTP 400.
  • MCP tool and namespace names replace - with _ (mcp__my-server__x becomes mcp__my_server__x), and colliding tools of one server get a hash suffix, so a script cannot call the wrong tool. See Breaking changes.
  • A deep TMPDIR no longer makes the extension host fail to start on macOS and Linux.
  • tool_execution_update events reach extensions, RPC and JSON consumers with Pi's partialResult shape, and a tool's result members keep the order the tool wrote them, in every SDK.
  • An extension tool's prepareArguments runs before PiG validates the model's arguments, as in Pi, so an extension that spreads Pi's createEditToolDefinition accepts the legacy flat {path, oldText, newText} edit again.
  • The tool calls of a parallel batch start in the model's order in extension tools, as in Pi.
  • Anthropic strict tool use sends a tool non-strict when its schema uses a keyword Anthropic rejects, and context overflow detection recognizes Z.AI CN Prompt exceeds max length errors.
  • A Retry-After header with an unparseable value makes the provider retry with exponential backoff instead of immediately.
  • Prompt submission no longer slows down with session length.
  • RPC mode loads extensions before it resolves --model and --models, so a model registered by a -e extension can start an RPC session.
  • MCP OAuth sign-in no longer fails with Invalid client_secret, Invalid scope and similar errors when a token or client registration response sends "" or null for an optional field, and expires_in: null no longer marks the token as expired. An empty scope="" in a WWW-Authenticate challenge or an empty scopes_supported no longer overrides the next scope source. MCP list pagination ends at a nextCursor of "" or null instead of failing with a duplicate cursor error.
  • Deferred MCP tools that tool_search loaded are active again after /reload, and after resuming a session, once their server reconnects before the next prompt.
  • OpenAI Responses requests no longer fail with Expected an ID that begins with 'ctc' when they replay grammar tool calls, such as codemode, from another provider or a gateway like Radius.
  • Typing / after leading spaces offers slash-command completions, and colors no longer bleed past mouse selections and search highlights in fullscreen mode.
  • Leaving fullscreen with fullscreenExitOutput set to "transcript" keeps the blank line before the resume hint, and background-colored message rows end at their padding.
  • Codemode models and ctx.modelRegistry work in interactive mode and after /new or a resume.
  • Messages that tell the user to run a command say pig config and pig login, not pi config and pi login, and a start that fails only because an extension did not build prints the -ne hint.

The complete list is in CHANGELOG.md.

Breaking changes for extension and SDK authors

PiG has no compatibility shim for these. Each follows the Pi API named in the changelog.

  • Go tool progress API. agent.ToolUpdateCallback is func(partial agent.AgentToolResult), and agent.ToolExecutionUpdateEvent carries PartialResult agent.AgentToolResult instead of Content string and Details any.
  • Go ai model API. CreateProviderOptions.Models and FetchModels use []ai.AnyModel (convert with ai.AnyModels(models)). ImagesModel is ImageModel, ImagesAPI is ImageAPI, and ImagesCost and the ImagesModels collection are removed (use Models.GenerateImages). CreateProvider panics when it gets no chat, image or classifier implementation, as Pi's createProvider throws.
  • Go extension host API. extension.EventBus is Emit(channel, data) and On(channel, handler), returning the unsubscribe function. extension.API gains GetSettings, the MCP and virtual-model registration methods and OnProviderStreamEvent. coding.ProviderConfigInput.Models and the value RefreshModels returns are []ai.AnyModel. Session.Steer and Session.FollowUp return (QueuedInputDisposition, error), and PromptOptions.PreflightResult is func(PromptDisposition), called only for an accepted prompt.
  • Settings. Settings.DefaultTools holds the raw defaultTools list; read the selection through SettingsManager.GetDefaultTools().
  • Built-in paths. The sourceInfo.path of a built-in tool is builtin:<name>, and the llama.cpp extension is builtin:llama.cpp with source builtin.
  • Rust SDK. ToolDefinition and ToolInfo gain Pi's tool fields (exposure, namespace, annotations and, on ToolDefinition, output_schema, default_active and prepare_loadout); build a ToolDefinition with ToolDefinition::new. The Provider struct gains generate_images and classify.
  • Go MCP OAuth library. McpOAuthCredentialStore.ForServer, Tokens and Remove take the server name before the server URL, as in Pi 1.0.0. mcp/oauth.OAuthIssuerMismatchError.Received is a *string, nil when an authorization response lacks the iss its server promised. mcp/oauth gains StepUpScope, the Iss and AuthorizationServerMetadataURL flow options and DiscoveryOptions.AuthorizationServerMetadataURL. Session.ReapplyActiveTools applies a selection again after a mode rebuilds the agent's tools.
  • Codemode scripts. Reading a tool or global-namespace member that does not exist throws instead of returning undefined. Replace typeof tools.name with "name" in tools.
  • npmCommand setting. An npmCommand that wraps more than one supported package manager without a -- to choose one (for example ["corepack", "npm", "pnpm"]) is an error (Ambiguous npmCommand package managers: npm, pnpm), as in Pi. Git package dependencies install with the arguments of the detected package manager.
  • MCP tool names. - in an MCP server or tool name becomes _. Update --tools, defaultTools, -xt and Piglet tool-scope entries that name an MCP tool with a hyphenated server. Two server names that differ only in - and _ are now rejected.
  • Default models. The defaults of openai-codex, fireworks, together and opencode-go change as listed above.

Windows

Windows remains a preview. The release workflow smoke-tests the windows-amd64 zip; the windows-arm64 zip and npm package are published without a native smoke test, and a standalone pig.exe does not update itself (D39). See the Windows setup page for what is not yet covered.

Known issues

  • RPC: a command sent immediately after agent_end can be answered before agent_settled is emitted, and an extension command's prompt response can be lost when stdin closes at once. Both are ordering races found under CPU load on a two-core machine; they affect only clients that send a command on agent_end or close stdin right after a command.
  • quietStartup: "header" hides the resource listing but PiG does not print Pi's Model scope: startup line in any mode, so the setting differs from Pi only by that missing line.
  • A start that loads PiG Standard's own piglogin extension, or any extension that registers /sprite, replaces the built-in pig-login and prints Pi's replaced-built-in warning. The warning is correct; the replacement is intended.
  • Under heavy CPU load, /clone right after the first answer can report "This session has not been saved yet" and draw the error above that turn's message. Run /clone again.
  • One Pi test file, 2860-replaced-session-context.test.ts, is only partly ported, because no extension SDK exposes withSession. See docs/parity/KNOWN-GAPS-0.3.x.md.

Verification

PiG 0.4.0 was checked against Pi 1.0.0 with the parity scenarios and upstream tests listed in the repository. The release workflow smoke-tests the linux/amd64, darwin/arm64 and windows/amd64 archives on native runners; linux/arm64, darwin/amd64, windows/arm64 and android/arm64 are cross-built and published without a native smoke test. The release evidence (checksums, SBOMs, inventory validation, vulnerability reports and provenance attestations) is attached to this release.

Thanks

New in this release:

  • @nicholas-recht: reported packed Go extensions losing the terminal height after /reload (#115) and contributed the first fix (#116). A follow-up in this release covers the remaining cases.
  • @worldofgeese: reported the intermittent Windows Node extension cell publication failure (Access is denied on rename) (#106). The fix is in #110.
  • @jkerdreux-imt: contributed the fullscreen Kitty keyboard fix (#105) and the theme color fix (#108), landed together in #109 after 0.3.1.

Carried over from 0.3.1, which this release builds on:

  • @nguyen-tran-100x: reported --exclude-tools being ignored in RPC mode (#101).
  • @sj0n: reported a Node extension command crashing its extension (#103).
  • @nickdeighton: reported the startup panic when an extension footer is wider than the terminal (#104).
  • @ShoichiTect: reported transport errors saying fetch failed where Pi says Connection error. or Request timed out. (#86).
  • @baggiiiie: reported pig config not closing on Escape (#89) and contributed the fix that keeps the shell prompt below the selector after it exits (#90); reported extension widgets and footers disappearing after a fullscreen resize (#121) and contributed the fix (#122), which shipped inside #114.
  • @SamarthBoranna: reported pig piglet show --effective rejecting local paths (#98) and contributed the fix (#99).