Skip to content

v0.1.0

Choose a tag to compare

@thecodacus thecodacus released this 07 Oct 14:59
66a35a4

The first tagged release of Pithagoras, a self-hosted web portal for the pi coding agent. It covers everything from the start of the repository on 28 July 2026 up to the voice orb fix merged in #92 on 7 October 2026, after a full review of the code in #88: 602 commits and 55 merged pull requests.

Upgrade notes

Read these before deploying over an existing install.

  • Node 22 or newer is required for native installs. better-sqlite3 moved to 13 (#23), which needs Node 22+. The Docker image already runs Node 22.
  • A password is now required to start. Without PORTAL_PASSWORD the portal exits, unless PORTAL_ALLOW_NO_PASSWORD=1 says a reverse proxy authenticates in front of it. When it does run without a password it binds loopback only, unless ALLOW_OPEN=1 (#22, #41).
  • Every folder directly under the workspace root is now a project and is listed on the Projects tab (#41).
  • PORTAL_CONTAINER_NAME must match container_name when the portal runs in Docker. Both shipped Compose files set it to pithagoras.
  • The database is upgraded on start, after a check and a backup (#51). An existing database is checked with PRAGMA quick_check, copied to DATA_DIR/backups/ (the newest two are kept), then upgraded in one transaction. A maintenance page answers on the portal's port meanwhile. A damaged database, or a disk without room for the backup, is left unchanged and the page says why and how to repair it. PORTAL_UPGRADE_BACKUP=skip upgrades without the backup. See the new Upgrading guide.
  • One agent becomes many (#52). An existing install comes up with one agent, its current Home, and every channel stays on it.
  • The built-in aria voice is gone (#53). An install whose saved voice is aria won't speak until another is chosen. Add the same WAV and transcript as a reference clone with Add voice and pick it.
  • The browser is now the portal's own tools (#56). The first start replaces the Playwright MCP entry the portal wrote, and carries every browser permission (the default, each project's and each chat's) to the new tools. A Playwright MCP added by hand afterwards is left alone.
  • pi-mcp-adapter is pinned to 2.18.0 (#44). 3.x reads a different config file, so the portal's MCP servers, the browser included, would vanish. An install made while it was unpinned can be pinned with pi install npm:pi-mcp-adapter@2.18.0.
  • Managed voice moves to audio.cpp v0.9.0 (#57). The first voice start after the update rebuilds it once; models are not downloaded again. The setup script's VOICE_ASR_CPU_CONFIG is now VOICE_CPU_CONFIG.
  • API changes: GET /api/voice/gpus is replaced by GET /api/voice/hardware (#49), and PUT /api/agent/orb by PUT /api/agents/:id/orb (#52).
  • Saved speaking instructions are kept as they are. Reset to default under Settings → Add-ons → Voice picks up the new built-in text: a sequence of actions is announced once (#55), and the agent says, now and then, a short line of what it found or is trying next (#68).
  • The voice rule is in every conversation while voice can speak (#69). With the voice add-on on and a speech engine installed, the rule for spoken replies (about 3,300 characters) is in every conversation's system prompt from the start, so a local model no longer reads a long conversation again before its first spoken reply. Typed messages keep normal chat formatting.
  • The first spoken call skips thinking only for the providers listed under Settings → Add-ons → Voice (#66). With no list saved, it follows the kind each provider has on the Providers page, so a llama.cpp server is covered under any name (#88).
  • llama-swap models saved before #63 keep the router's name. Edit → Ask again → Save on the provider gives each its own.
  • Sign-out now revokes the login on the server, so a copied cookie stops working too. Logins made before this still sign in normally.
  • The database moves to schema 3 on the first start (#88), through the same check and backup. Rules for subagent, routine_create, routine_update and routine_run saved for anyone but the heartbeat are removed: they never applied to other people and would have given them the primary user's rights.
  • WORKSPACES_DIR is required in .env (#88). Compose no longer falls back to /root/repos; set it to that if you relied on the old default.
  • PORTAL_PASSWORD=change-me no longer starts, and a new or changed password needs at least 8 characters (#88). A portal that already ran with a shorter one keeps starting, with a warning.
  • The Portainer stack mounts PORTAL_DATA_DIR as docker-compose.yml does (#88). If your stack sets it, copy the old volume into that folder first, or remove the variable. The Upgrading guide has the command.
  • Channel messages need a sender once a primary user is named (#88). A webhook without from in its body or a senderId on the channel, such as a cron job or a home automation, gets a stranger's reply instead of running as you. A third-party channel package has to pass from to ctx.ask.
  • Only approve and always approve a request (#88). "yes" or "ok" answer the question without approving anything.
  • A relative rule such as inner/* matches the path as written; a path that reaches it through a link is refused (#88).
  • Smaller changes (#88): a slash command in a channel chat runs only when the primary user spoke last; a chat idle for 20 minutes is let go and started again from its saved conversation on the next message; deleting an agent with its folder deletes its routines, and deleting an agent or project stops the background jobs in its folder; pictures from other sites in replies and notes are not loaded; the pi in the image is the version the lock names; deploy/cortex-voice is gone from the repository.

Known issues

  • #42: deleting a project now asks first when its repository holds uncommitted changes, unpushed commits or stashes, and the Files panel refuses a delete that would lose them (#49). A project with no unsaved work is still removed with its folder, which with the shipped Compose file is a real directory on the host.
  • #43: a few server tests fail when run as root or under a loaded parallel run, and on macOS. The code under test is not affected.

Agents

  • Several agents, each with its own home folder, SOUL.md, PrimaryUser.md, memory, chats, avatar and voice (#52). The Agent page shows a card per agent, and a new one is set up with the same two-question wizard. Deleting one asks whether to keep its folder; kept, an agent made later under the same name picks it up again.
  • Channels and routines choose their agent with Talks as and Runs in (#52).
  • Heartbeat (#54). An agent can look around on its own, from every 30 minutes to once a day with quiet hours, guided by a WATCH.md of what to keep an eye on. A look runs read-only under a new heartbeat role, may run only the commands you allow, and leaves notes in the agent's Activity tab. Urgent ones also go to the routines' channel. Off until an interval is chosen.
  • The voice orb as an avatar (#48, #50). Personalities that set how it moves (Balanced, Calm, Lively, Playful, Focused), palettes and a colour per state, eyes, hats and props, a glossy or plush finish and seven patterns, all in a tabbed customizer with a live preview.

Chats and sessions

  • Home and projects. New starts a chat straight away in the agent's home. A project is a folder under the workspace root with its own AGENTS.md instructions. Chats are listed by folder in the sidebar and on the Sessions page, with a folder per agent.
  • Reply stats (#61). Beside each finished reply's Copy: how fast it was written, how fast its prompt was read, and the tokens in and out, e.g. 35.9 t/s · prefill 544 t/s · 12,467 in · 41 out. The tooltip adds the times, the cached part of the prompt and accepted draft tokens.
  • Switching models mid-chat works (#62). The earlier model's thinking is no longer handed to the new one as answer text, which made every reply after a switch land inside the thinking block (#60).
  • Message actions. Resend, edit and delete sent messages, and each message keeps its earlier versions. Agent chats can be renamed and deleted (#35).
  • Long conversations open at their end and load earlier messages as you scroll up. The view follows the agent only while you are at the bottom (#37, #38).
  • Steering. A message sent while the agent works steers the run by default. Stuck runs are fixed, and model errors are shown in the transcript.
  • Every slash command and extension message shows its result in the chat. The character that opens the command palette is a setting, / by default (#49).
  • Context and compaction. A context window can be set per model and as a default. Compaction shows while it runs, can be stopped, and has a slider for how much of the conversation it keeps (#10, #12).
  • Thinking level. Models with only on and off show a switch, and the last level picked is saved (#34). The live reasoning preview no longer re-wraps while it streams (#58).
  • Resizable message composer (#6), and a Copy on the answer to each question (#49).
  • Queued channel notes are kept until their turn is accepted, and dialogs and tool results are matched correctly (#24).

Panels beside the chat

  • Files browses, edits, uploads and downloads the chat's folder, and follows what the agent reads and changes (#2, #41).
  • Git covers changes, history, branches and pull requests through gh.
  • Subagents and background jobs show beside the chat. An extension can speak a small subagent protocol to be watched and messaged.
  • Session canvases, markdown documents the agent writes, have a panel of their own (#18). A streamed write counts as one revision (#47).
  • Panels dock to the left, right or bottom edge, or float.

Models, tools and extensions

  • Model providers are set up from Settings, with a setup assistant, provider status, discovery of OpenAI-compatible servers by address, and a searchable package catalogue.
  • Picking a model sets its own provider (#65). A chat on a provider since renamed, or one moving to another provider's model, no longer asks for a model that does not exist ("Model not found").
  • The model menu stays current (#59). The list cached in the browser expires after an hour and is dropped when a provider is saved or removed, and a refresh icon fetches it at once. llama-swap aliases show their own names (#63).
  • llama.cpp prefill progress shows as a live bar while a long prompt is processed.
  • Tools per chat and per project. Choose which tools a chat may use, what every chat starts with, and what a project's chats start with (#49). Tools of a switched-off extension are no longer listed.
  • Extensions can be switched off without uninstalling, and their settings edited. Skills and MCP servers are managed from Settings.
  • People and roles. Who the agent talks to, rules allowed per person or role, and an Audit page for what was refused, allowed or turned away, which can be cleared (#49).

Browser

  • The portal's own browser tools (#56) on the same Chromium. A page comes back as a view of what is on screen, capped at about 3k tokens, instead of the whole page: a long Wikipedia article went from about 130k tokens to 1.7k. Clicking and typing answer with what changed, and browser_find, browser_get_text and screenshots cover the rest. A ref whose element is gone is refused rather than clicked somewhere else.
  • Browser output is marked untrusted, including the Playwright MCP's direct tools, which were not before (#56).
  • The agent's cursor (#67). Before each click, typed text, choice, key or scroll, a see-through arrow glides to the element along a slight arc and says what it is about to do, so you can follow the agent in the browser. It keeps its place across pages, follows the light or dark theme, and is never in what the model reads. It never shows what is typed, stays in screenshots so a vision model sees where it is, and fades after two minutes without an action (#70). Browser → Show the agent's cursor turns it off.
  • Screenshot-first reading with coordinate clicks and scrolling for a vision model (#45).
  • The agent drives a real browser with your signed-in sessions, and it keeps rendering when nobody is watching.

Images

Opt-in under Settings → Add-ons; needs an OpenAI-compatible images endpoint, such as stable-diffusion.cpp's server (#58).

  • Generate and edit with the generate_image, edit_image and show_image tools. An edit takes several reference pictures (the first counts most) and an optional mask.
  • An Images page with Generate and Edit modes and a gallery of the pictures in every agent's home. References can be added from the gallery, a file, a drop or a paste, and reordered.
  • Per-picture settings: model, size, count, format and compression. With Stable Diffusion extra settings on, the seed, steps, negative prompt, strength and start-from-noise are sent in a form stable-diffusion.cpp reads.
  • An in-chat image viewer, and a preview that animates while a picture is being made. Time limits for generation and editing default to 5 minutes.

Voice

  • Voice mode with pictures both ways, tool cards, repeat, speaking speed, steering, push-to-talk, configurable shortcuts and separate resizable windows. It survives a reload (#18, #41). Replies render as markdown in speech bubbles (#49).
  • Choose the speech engines (#49): Breeze, Chatterbox or Kokoro for speech, Whisper base or small or Qwen3-ASR 0.6B or 1.7B for recognition. The installer checks the GPU and its memory first and suggests a combination that fits.
  • Kokoro (#57), 49 voices in eight languages, on the GPU in about 1 GiB or on the CPU at roughly a quarter of a second per second of speech. A host without a GPU can now give spoken replies.
  • Choose the GPU the managed voice service runs on, on a host with more than one (#46).
  • Uninstall for the managed voice service, which puts back the voice settings you had before (#58).
  • Talking while it works (#55, #68). A sequence of actions is announced once, not at every step, and now and then the agent says a short line of what it found or is trying next, so a long task is not silent. Speaking instructions are configurable, and a saved voice's description can be edited (#49).
  • A quick first reply (#66, #69). The first call of a spoken turn skips thinking for the providers listed under Settings → Add-ons → Voice, llama-swap included, and with voice on the rule for spoken replies is in the prompt from the start.
  • Dictation speaks into the message box without a spoken reply (#36).
  • The orb no longer flickers while it glides beside a window or into the dock (#92).
  • Managed voice services move into the portal's network namespace automatically (#30).

Opt-in features

Off by default, switched on under Settings → Add-ons.

  • Subagent tool with interrupt or background mode, a limit on subagents at once, and a model per chat.
  • Understory memory. The portal can run Understory in a container, with a Memory page showing the tree, search, log and graph. While it is on, MEMORY.md is not read.
  • Images, above.

Portal

  • An installable PWA, and a layout that works on phones (#25).
  • A visual overhaul with light, dark and system themes, and extra animations, on by default, switched off under Settings → This browser → Animations (#58).
  • A German translation. Every UI string goes through t(), and a test lists missing or stale translations.
  • Content Security Policy, login throttling, and validation of package and skill paths (#22).
  • The container executor maps portal volumes to host paths, runs the runner as the portal's user, and reclaims stopped session containers safely (#26, #27, #29).
  • Completed messages are stored instead of every streaming update, which stops the event log growing with each token (#19).
  • One client per session, so two requests on a cold session no longer start two pi processes (#13).
  • The Docker image builds the native SQLite binding correctly, and the Compose files parse again (#39, #40). Portainer deployments can pick a release with PITHAGORAS_VERSION (#51).
  • Documentation brought up to date, with guides for the interface, models and providers, agents, images, skills, upgrading, Git, the terminal, background jobs, memory and audit, and a fuller configuration and HTTP API reference (#20, #28, #51, #58).
  • Personal names and the production notebook are out of the repository (#53).
  • Licensed under the Apache License 2.0 (#64).

A full review before the release

#88 is the result of a review of the whole code base by area, with four lenses (bugs, security, clean-up, efficiency), followed by ten review rounds on the fixes, an end-to-end run against a real portal in each round, and a maintainer pass on top.

  • Security and roles. What a colleague, a guest or a channel may read, write, run or approve is held in one place, with path checks that follow links, one list of what pi and the MCP adapter load from a folder, exact approvals, SSRF and origin checks, and secrets kept out of logs and responses. The browser's own page now needs the portal login.
  • Runtime and data. A shutdown ends terminals and their jobs, idle chats are let go, routines and the heartbeat record a stopped run, and deleting an agent or project cleans up what belonged to it. A session's events are stored slimmer.
  • Chat. Streaming draws once per frame, rows redraw only when they change, and switching chats while a reply streams no longer leaks it into the next chat. Dialogs keep focus, and Settings asks before discarding unsaved changes.
  • Ops and docs. The image builds from the lockfile, Compose runs with init, CI publishes an image only after the tests pass, and the guides match the code. About 18,000 lines of tests were added.

Contributors

Thanks to @Piggidragon, @Shinigallo, @mozdyn and @psycmos for their contributions to this release.