Skip to content

v0.2.0

Latest

Choose a tag to compare

@thecodacus thecodacus released this 23 Oct 18:32
· 18 commits to main since this release
fbcbf31

The second release of Pithagoras, a self-hosted web portal for the pi coding agent. In 0.2.0 the portal is a core plus extensions: the terminal, Git, Files, Canvases, Media, the agent's browser, the voice service and Understory moved out of core into packages under extensions/, built on an extension framework that other packages can use too, with its types published on npm as @pithagoras/extension-api. On top of that come chat modes, Creator mode, an OS-level sandbox, OIDC sign-in, Signal, Documents, Kanban and a reworked guard. It covers everything from v0.1.0 on 7 October 2026 up to #302 merged on 23 October 2026, with this release's own pull request: 137 commits and 132 merged pull requests, 74 by @thecodacus and 58 by @Piggidragon.

Upgrade notes

Read these before deploying over an existing install. The Upgrading guide has a section for a portal at 0.1.0 and one for each step below, with the steps for going back to 0.1.0.

The database and the first start

  • The database goes from schema 3 to 11 in one start. It is checked with quick_check, backed up as backups/portal-v3-<date>-<time>.db, and upgraded in one transaction, behind the Upgrading page. A hand-off to an extension that cannot finish keeps everything and is tried again at the next start. 0.1.0 does not open the upgraded database: going back is putting that backup back, with the steps in the guide.
  • Six extensions are installed on the first start: Terminal, Git, Files, Canvases, Media and the theme pack (#197, #203, #205, #215, #223, #226, #256). They are ordinary packages afterwards, and one you remove stays removed. PORTAL_DEFAULT_EXTENSIONS=off installs none of them (#207).
  • Browser, Voice, Voice servers and Understory are not installed by the upgrade (#264, #273, #274). A portal that used 0.1.0's browser, voice service, own speech servers or memory installs them from Settings → Extensions → Discover → Comes with Pithagoras. Browser, Voice and Understory require Docker for extensions, which the install question lists and installs first (#260). What 0.1.0 kept for each waits in handoff/ until then, and their containers and volumes stay: each extension takes them over by name.
  • Until they are installed the agent has no browser tools and there is no Browser page, voice mode says its source is not running, and chats read MEMORY.md again.
  • Stored canvases become Markdown files in their chat's folder, canvases/<title>.md (#223). A chat whose folder is gone has its canvases written to canvases/ in the data folder.

Things to change

  • The password moves into the database (#168). A PORTAL_PASSWORD in use is hashed and moved in once, then ignored: take it out of .env, Compose or the Portainer stack. Logins from before the upgrade are asked for the password once more. change-me is not taken. Going back to 0.1.0 needs the variable set again.
  • Addresses moved, with no aliases. Pages: /images is /x/pithagoras-media/media, /browser is /x/pithagoras-browser/browser, /memory is /x/pithagoras-understory/memory. Routes: the terminal, Git, Canvases, Files, Media, Browser, the voice service and Understory are under /api/x/<package>/…, and PUT /api/browser/allowlist is PUT /api/guard/allowlist. The guide has the full table. Reload open tabs after the upgrade.
  • Settings → Add-ons and Settings → Agent → Images are gone (#256, #274). Subagents is in Settings → Tools, voice mode's settings are at the end of Settings → Defaults, and Browser, Voice, Voice servers, Memory and Media each have a page under Extension settings. The allowlist is in Settings → Guard.
  • pi moves from 0.82 to 1.1 (#112). Installed packages keep their versions and may need their own update. pi reads AGENTS.override.md before AGENTS.md. Azure's provider is renamed from azure-openai-responses to azure: rename it in pi's auth.json, models.json and settings.json, or sign in again. pi-mcp-adapter stays at 2.18.0.
  • Node 22.19 or newer for an install from a checkout. The image runs node:22.
  • The Compose voice overlay is gone (#279): docker-compose.voice.yml and deploy/voice-multilingual/. Its containers keep running as Compose orphans. Once voice mode no longer uses them, remove them; docker compose up -d --remove-orphans removes the whisper one as well, so keep it where Voice servers listens to it. A start command that names -f docker-compose.voice.yml fails until the file is dropped from it.

What behaves differently

  • Voice (#273): the upgrade picks voice mode's source: Voice where the voice service was in use, Voice servers where your own addresses were, none where voice was off. Agents' voices stay. Breeze Python has no source any more, and speech at your own address carries over only for Kokoro. The voice container is given Docker's unless-stopped policy in place the first time Voice finds it, so it comes back after the host restarts (with Docker 29 or later; older Docker can leave it stopped after a restart, and the voice page then shows failed with Retry setup); the update in place needs Docker for extensions 1.2.0, which ships with this release and which Voice requires (#298).
  • Browser (#173, #264): who may drive it is the tool switches now; a routine's Browser switch is written into its tools once. A new routine has the browser tools, as a chat does; to keep the old default, switch the pithagoras-browser group off in each new routine's Tools, or off in Settings → Tools and back on where it is wanted. The browser's own ports listen on 127.0.0.1 (BROWSER_BIND); a container from 0.1.0 keeps listening on every interface until it is removed and installed again.
  • The guard (#111, #253, #255, #271): a chat is held only after a result that looks like a prompt injection, so no chat 0.1.0 held is held after the upgrade. A held call is asked of the primary user, and refused where nobody can answer. MCP output is marked untrusted however a tool is called, unless its server is trusted in Settings → Guard; where Understory was in use, the upgrade trusts understory once. A heartbeat's rules for subagent and the routine tools are removed. A routine's Injection guard switch becomes per-rule exemptions, and one that was off is exempt from every rule.
  • Code mode is put in the tool defaults as off (#178). Every chat is in Standard mode (#283).
  • Extension dialogs have no 300-second fallback (#157): where nobody can answer, a dialog answers at once with no value.
  • MCP servers taken out of mcp.json by hand are noticed from now on (#295). The portal records the servers it sees the first time it looks (the new mcp_configured setting) and judges nothing then, so a tool left by a server removed by hand before the upgrade stays listed. Add the server again in Settings → MCP and remove it there to clear it. The same clears the tools of a dotted server name removed before (#296).
  • Dependencies moved: sharp is Media's and playwright-core is Browser's (#256, #264). From a checkout, npm run build installs each extension's packages.
  • The image adds sudo, ripgrep, fd-find and file, the user pi-tools and the group pi-sandbox (#72, #248). The sandbox is off until switched on.

New environment variables, all optional: BROWSER_BIND, PORTAL_DEFAULT_EXTENSIONS, UNDERSTORY_LLM_PORT, UNDERSTORY_LLM_HEADERS_TIMEOUT_MS, UNDERSTORY_LLM_BODY_TIMEOUT_MS, PORTAL_FETCH_HEADERS_TIMEOUT_MS, PORTAL_FETCH_BODY_TIMEOUT_MS, PITHAGORAS_LOG_LEVEL, PITHAGORAS_LOG_LINES, PITHAGORAS_LOG_CHARS, PORTAL_UPDATE_CHECK, PORTAL_UPDATE_RESTART and FFMPEG_PATH. None was removed. UNDERSTORY_LLM_PORT (3801) must be free on the host where Understory thinks with the chat's model.

Known issues

  • #43: some server tests still fail on macOS, among them the upgrade test that holds the portal's port on 127.0.0.1, which macOS lets the wildcard address bind as well. The upgrade itself is not affected.
  • #269: the Update button works for git checkouts only; a Docker install gets instructions instead.
  • pi's find with the sandbox on: pi 1.1's find passes --no-require-git, which the image's fd 8.6 (Debian's fd-find) does not have.
  • A server half in a process of its own is not a security boundary (#289): it runs as the same user, with the same environment and the Docker socket. With the sandbox off, a Creator mode agent acts as the portal's user (#290).
  • The container executor has no tool switches and no chat modes, and ignores the voice model (#139, #178, #283).
  • Logs are kept in memory only (#140).
  • Installing Browser, Voice or Understory through POST /api/packages without Docker for extensions leaves them waiting with nothing said, while /api/extensions lists them as switched on. Settings → Extensions installs Docker first.
  • After the Upgrading page closes, the port refuses connections for about half a second before the portal listens; a refresh that lands then shows a connection error. Refresh again.

Extensions and the extension API

  • A pi package can be an app of the portal (#169): a browser half drawn through slots (chat.panel, chat.header.action, chat.tool, chat.composer.above, chat.overlay, chat.background, voice.window, nav.page, settings.page), a server half with routes under /api/x/<package>/, chat lifecycle, pushes, chats.extend, holds and commands run as the chat's agent, and themes in package.json with no code.
  • @pithagoras/extension-api (#197, #282): types, buildExtension, one React for the page, and a boundary check between core and extensions. It is published on npm with the portal's version, 0.2.0. portal.version stays 1.
  • More of the API: named panels, chat.tool.action, portal.shortcuts and jobs.show (#201); maximizable and keepLoaded voice windows and useShown (#225); files a tool gives through details.files, shown under the card, in channels and in voice mode, for every tool (#230); calls being written in the chat feed (#231); provide/use and requires/optional with semver ranges, with start order, restarts and blocked starts handled by the portal (#232); files.create/open, http.send, tools.offer, chats.reload, folders.*, tool-declared approvalFiles and settings search entries (#250); portal.handoffs, ui.ImageViewer, ui.Modal, portal.navigate and settings pages with titles and icons (#256); a tool's guard declaration, ChatLife "finished" and portal.tools.exceptions (#259); install plans, useButton, fullscreen and bleed (#260); portal.voice.provide/choose (#266, #273); portal.models, portal.mcp and portal.memory.hold (#267); portal.modes.provide and modes that ask first (#285); portal.extensions.install/remove/status and portal.audit.record (#288).
  • Loading and running: extensions load in parallel, and their files are served compressed with ETags and preloads (#244). Chats take an extension change at their next step, with no /reload (#270). Open pages follow installs within one poll, and browser-half failures are reported; ?extensions=off opens the portal without them (#275). A server half that crashes never stops the portal or a chat: it is held off and shown in Settings → Extensions with its last lines (#276). Every file of a half takes effect when it is installed again (#277). pithagoras.isolated runs a server half in a process of its own, with a watchdog for hangs and memory (#289), and its log lines are held to 8,000 characters however the pipe splits them (#297).
  • Tool lists no longer show the tools of a server half whose package is switched off or uninstalled; their switches are kept for when it comes back (#294).

The shipped extensions

  • Installed on the first start: Terminal (#203), Git (#215) with a repository picker (#144), Files (#226) with rendered Markdown (#145), a Following toggle (#133), paths hidden on demand (#143) and a Refresh that reads the open file again, keeping unsaved edits behind the changed-file banner, with the agent's writes to it shown whether or not the panel follows (#300), Canvases (#223), Media (#256) and the theme pack: Forest, Ocean, Rose, Amber, Mono, Dusk and Warm (#189, #205, #243).
  • Media, before and during the move: video generation through stable-diffusion.cpp with generate_video, show_video and a Videos view (#164), small gallery copies (#152), and edits that scale large pictures down instead of refusing them (#159).
  • Opt-in, under Discover → Comes with Pithagoras:
    • Documents (pithagoras-docs), by @Piggidragon: shared Markdown that you and the agent edit, with git history and restore, live merge, comment threads anchored to passages, @agent pings that wake the chat, a panel beside each chat, a word-processor editor, upload and download, rename and delete (#187, #191, #195, #196, #199, #204, #206, #214, #222, #224, #228, #231, #237, #247, #262).
    • Docker for extensions 1.2.0 (#233, #235, #249, #258, #265, #298): containers for other extensions, containers.update, and docker_* tools for the agent in its chat's folder, off until switched on, refusing privileged settings and binds outside the folder, run as the agent with the sandbox on. Settings → Docker offers Stop for a container Docker keeps restarting.
    • Browser (#264), Voice and Voice servers (#273), and Understory (#274).
    • Kanban (#261): boards on their own page and beside a chat, kept by you and the agents through kanban_* tools; agents cannot delete.
    • Creator mode (#288, #289, #290): a chat mode, with a warning, in which the agent writes an extension in the chat's folder and installs it with extension_install. The install copies the draft into Creator's data folder, runs it in a process of its own, and records it in Audit with a digest; the agent calls the new tools at its next step.

Chats and tools

  • Chat modes (#283, #284, #285): Standard and Code mode, picked above the message box before the first message. Settings → Modes holds modes you make, with instructions, a model and effort, the code tool and a tool layer. Extensions can offer modes too.
  • Tools: the lazy-tools gateway tools with search, describe and call (#153, #194); code mode as a tool switch, off by default (#178); built-in tool texts about a third shorter (#186); tool switches for agents and routines, in the order Settings, agent, project, routine, chat (#100); a switched-off MCP tool stays off through mcp and mcp_script (#98); pi 1.1's built-in tools labelled (#177).
  • MCP: a server taken out of mcp.json outside the portal is noticed and its tools forgotten (#295). Tools of a server with a dot in its name are grouped, withdrawn and trusted by the prefix the adapter really gives them, and a server's toolPrefix is honoured for trust (#296).
  • The agent: ask_question in browser chats (#157); routine tools in every agent's home chats, and a routine runs as the agent that made it (#239); a project can work with an agent or stay a blank slate (#127, #154); an agent can be picked for the New button (#137), and the Agents page's New conversation is the same chat as the + button (#136).
  • The message box and transcript: queued prompts can be edited, deleted or sent at once (#158); extension notices show as text (#134); the conversation slides over when a panel docks (#229).
  • Channels: Signal, through a signal-cli daemon, with pictures both ways (#163).
  • pi and providers: pi 1.1.0 (#112); with EXECUTOR=container, a chat's container gets only the key of the provider it runs on (#108); 30-minute waits for every fetch in the portal's process, so MCP calls can run past five minutes (#119); a local package removed by its full path (#240).

Voice

  • Voice mode listens and speaks through a provider (#266, #273). Voice owns the voice library, clones, engines, the GPU and lazy loading; Voice servers connects your own OpenAI-style or whisper.cpp servers.
  • The voice container comes back after the host restarts (#298): it is unless-stopped, an existing one is updated in place, a failed setup shows its log while Docker retries, and Stop ends the retries. This needs Docker 29 or later; older Docker can leave it stopped after a restart, and the voice page then shows failed with Retry setup (The voice service).
  • Voice conversations can be switched off in Settings → Defaults → Voice mode while dictation stays (#302). The voice-mode button and shortcut go, voice mode's routes refuse, and the spoken-reply rule leaves the system prompt. It is on by default, so nothing changes until it is switched off.
  • Earlier in the release: a voice model of its own (#139), a dictation model and endpoint of its own (#138), a model error spoken and shown (#135), and holding the orb to talk with push-to-talk on (#146).

Memory

  • Understory is an extension (#274), holding the agent's memory through memory.hold, with its MCP server set through portal.mcp and the chat's model reached on a loopback port.
  • The memory graph is one 3D view with five looks, query paths and touch (#142), and the Memory page has the Librarian chat (#148).
  • Understory's model waits are 30 minutes and answer 504 on a timeout (#103), and each project has a memory switch (#154).

Guard and sandbox

  • An OS-level sandbox (#72), off by default, in Settings → Sandbox: each agent runs as its own user with the path rules held by the kernel, trusted commands run as pi-tools, and agent skills live in <home>/skills. With it on, pi's read gives a vision model a picture as a picture (#248), and Docker work in a chat's folder runs as the agent (#249).
  • Files are handled by descriptor (#247, #252), and context files (SOUL.md, PrimaryUser.md, MEMORY.md, TEAM.md, AGENTS.md, CLAUDE.md, WATCH.md) are read as the agent, never through a link (#254).
  • Taint and held calls: a chat is tainted only on a suspected prompt injection, with Trust it and Remove the turn (#111); after a taint, a held call is asked of the primary user (#271).
  • Rules (#253): MCP output is outside content however it is reached; persist, upload and pipe-to-shell catch more; heartbeats get no secrets, no writes to the agent's own files, and no subagent or routine; a shell lexer replaces a regex, with fewer false refusals.
  • Settings → Guard (#255, #259): per-rule routine exemptions, trusted MCP servers, unspent approvals with Revoke, trusted results with Un-trust, and the allowlist.

Settings and the page

  • Extensions in two views, Installed and Discover, in a Settings dialog that uses the screen (#180, #185); extension pages grouped in the sidebar and on the rail (#241); the rail's groups fold and stay as you left them, with the group of the page you are on held open (#301); Docker listed by its name and icon (#257).
  • Settings → Logs with a download (#140), and a log level with debug lines (#176).
  • Authentication: the password in the database with a first-start setup page (#168), and OIDC single sign-on (#162).
  • About gains Update (#166) and Restart (#190).
  • The Agent page's and the avatar studio's tab strips no longer scroll a pixel up and down (#109).

Deploying

  • OIDC sign-in, set up in Settings, with PKCE and checked ID tokens; "OIDC only" can be undone with npm run sso-only-off (#162).
  • Update (#166): for git checkouts with a clean tree; with PORTAL_UPDATE_RESTART=exit the service manager restarts the portal, and a notice shows when main has new commits. Restart (#190) needs a supervisor and exits with code 75. GET /api/health needs no login.
  • BROWSER_BIND (#173) and PORTAL_DEFAULT_EXTENSIONS (#207).
  • The server and the web app are in core/ (#198): a checkout starts with node core/server/dist/index.js; server/dist/index.js, reset-password.js and sso-only-off.js still work, as files that load the new ones.
  • Pin this release with PITHAGORAS_VERSION=0.2.0 in the Portainer stack, git checkout v0.2.0 for docker-compose.yml, or the image ghcr.io/thecodacus/pithagoras:0.2.0.

Docs

  • New guides: the sandbox (#72), Building an extension (#169 and after), Documents, Docker (#233), Kanban (#261), Media (#256), the voice service and Voice servers (#273), Creator mode (#290), Signal (#163), and the README of @pithagoras/extension-api (#282).
  • Upgrading has a section for a portal at 0.1.0, one for each step of this release, and the steps for going back to 0.1.0.

Contributors

Thanks to @Piggidragon, who wrote 58 of the 132 pull requests in this release and opened nearly every issue that came in from outside: Documents, OIDC, Signal, the password in the database, Update and Restart, Videos and the Media area, the tools gateway, code mode, Logs and the log level, BROWSER_BIND, the memory graph and the Librarian, ask_question, queued prompts, project agents, agent and routine tool switches, the MCP switch fix, the fetch and Understory waits, the voice fixes, the Warm theme, the sidebar groups, parallel extension loading, routine tools, many of the test fixes, and the issues behind Files' Refresh, the folding Settings rail and dictation without voice mode.