OpenMuse 0.2.0
The first release meant for other people's phones: the Muse-style app with Feed, Ideas, Goals and Library, scoped approvals, artifacts, push notifications, a browser view with take-over, reminders and routines, the app in 简体中文, tool_mode = "auto" so small local models work, and openmuse doctor. Verified end to end with DeepSeek V4.1 Flash and with qwen3:8b, llama3.2:3b and gemma3:4b on Ollama; CI on Linux, macOS and Windows.
Added
- Approvals are scoped capabilities. An approval is bound to a tool and a target (
shell:git,send_email:alice@example.com,web_fetch:api.github.com) and lasts once, for this task, this session, 24 hours or always. Cards show the purpose; a Permissions list revokes any grant. Calls with warnings (arm -rf, acurl | sh) are never covered by a grant. - Muse-style app shell: Chat · Feed · Ideas · Goals · Library tabs, an avatar menu with Approvals, Activity, Permissions, Upcoming, Memory, Connections and Settings.
- Artifacts: the agent is asked to answer with files (HTML pages, Markdown, CSV) when the result has a shape; every file any tool writes shows up as a card that opens in a sandboxed in-app viewer. The Library lists them.
- Connections screen and first-run setup: model and API key (straight into the vault), email (IMAP/SMTP), browser, MCP servers; connection tests;
app-settings.jsonlayered overconfig.toml. - Proactivity dial (Off / Low / Default / High), quiet passes (a background pass with nothing to say is one muted line, not a message) and quiet hours.
- Goals with categories, target dates and an overdue flag, check-in reminders (
daily 08:00,weekly mon 09:00, …) delivered as short messages, and plan proposals the agent makes and the user accepts or dismisses instead of silent rewrites. - Sentinel hardening: subprocesses get a scrubbed environment (no API keys or tokens);
python_executereads the code and escalates to SENSITIVE for network, processes, environment access, deletion or paths outside the workspace;web_fetchfollows redirects itself and refuses hops into private networks; any call with warnings asks even inautomode unless an explicitallowrule applies;agent.extra_rootsfor files outside the workspace. SECURITY.mdwith the threat model and reporting process;CODE_OF_CONDUCT.md; issue and pull request templates; Dependabot.- CLI:
/permissions,/revoke <key>;goals add --category/--due/--check-in,goals list --category. - Reminders and routines. "Remind me at six to call mum" and "every weekday at 07:30 summarise my unread mail": a
reminderstool, an Upcoming → Reminders & routines section in the app (add, run now, cancel),openmuse reminders list | add | cancel, andGET/POST /api/reminders. A reminder is one short message at that time in the chat it was set from; a routine is a background run with tools. A named time is kept whatever the proactivity level or the quiet hours. - Web Push notifications and an app badge. The phone buzzes when your Muse needs an approval, has a question, finished a background pass worth surfacing or it is check-in time — standard Web Push (VAPID) through the browser's own push service, no account with anyone; the icon shows how many cards are waiting. Needs
https://orlocalhost. - Browser view. When the agent browses, one card per run shows the page after every step, with what just happened ("Clicked 'Sign in'"). Tap it for the full view; Take over puts you at the controls (tap to click, type, Enter, open a URL) and Hand back returns the page to the agent, which is told what you did. That is how a login happens without a password passing through the model. The
-browserimage tags (docker build --build-arg WITH_BROWSER=1) bundle Chromium. - A file named in a reply opens on tap. Inline code and relative links that name a file made in the chat render as a chip that opens the in-app viewer, so "saved it to
kyoto-notes/packing-list.html" is the link. - OpenMuse on a simulated phone:
demo/mobilegym/installs OpenMuse as a native app on MobileGym, a browser-hosted Android simulator — launcher icon, setup page, and a bridge that turns approvals, questions and background results into notifications in the shade.docs/demo.gifshows one real task start to finish. ?tab=goals(feed, ideas, library, connections) opens that tab directly, like?thread=opens a chat.- The app in 简体中文. Settings → App language: Auto (follows the browser), English or 简体中文, per device; dates and relative times follow. Separate from the agent's reply language. No i18n library: the English text is the key,
web/src/i18n/zh-CN.tsthe translation, and a unit test fails when a string in the app has no translation — adding a language is one dictionary file. openmuse doctor: config file, data dir, model and key state, tools, connectors and one call to the model on one screen — the thing to run first and to paste into a bug report.openmuse --version.tool_mode = "auto"(the new default): the API's function calling, and when the endpoint rejects thetoolsfield — Ollama for a model without a tool template, vLLM without a tool parser — tools are described in the prompt for the rest of the run. Prompt mode also accepts the```tool_call/```jsonfences small models emit instead of the tags.scripts/provider_check.py: five everyday tasks against any model, one line each; results for Ollama models indocs/configuration.md.- Small-model repairs: a reply that is a bare JSON object naming a tool counts as a tool call in native mode too (Llama 3.x); JSON arguments may contain real newlines;
files.writeturns a one-line text with spelled-out\ninto lines.qwen3:8b,llama3.2:3bandgemma3:4ball pass the provider check. - CI runs on Ubuntu (Python 3.11–3.13), macOS and Windows, type-checks with mypy, lints and unit-tests the web app (ESLint, Vitest), and publishes
ghcr.io/openmuseagent/openmusefor amd64 and arm64.
Changed
- Sessions are repaired on load: tool calls that never got a result (the app restarted mid-call) get a placeholder result so providers accept the history; stale approval and question cards are marked expired. A tool call whose arguments were cut off in transit stays in the history as
{}instead of poisoning every later request. - For this task on
shellcovers the tool for the rest of the run, not only the programs in the current command — one decision instead of three forgit clone, thenls | wc, thensort | head. Recipients and hosts stay bound; warnings still stop every call. - A background run is pushed once, after its last word (
final: trueon the timeline event), not once per narration step. - The system prompt asks the agent to look in the workspace before searching the machine, to quote file contents only from tool output it actually received, and to answer with a file when the result has a shape.
- The web app no longer flashes the empty-chat prompts before the thread's history has loaded; no scrollbar gutters at phone widths; Feed previews strip Markdown.
Fixed
- Relative paths are POSIX-style on every platform (the same file was
notes/plan.mdon one machine andnotes\plan.mdon another); Windows CI is green. python_executeno longer stops for approval when the code spells out an absolute path that is inside the workspace.- Artifacts appear when the workspace lives inside the data directory (
~/.openmuse/workspace). - A page written in parts (write, then append) keeps one card and stays "new"; a streamed reply that turned out to be a tool call no longer leaves an empty bubble; a one-line reply delivered through
terminateright after a text reply is shown again instead of being taken for a repeat. - A macOS-only test race in the server suite.
- Ideas: one malformed item in the model's list (a missing colon, a real newline in a string, a reply cut off at
max_tokens) no longer throws the whole list away and shows the starter ideas instead.
Install
pip install -U openmuse # or: uv tool install openmuse
openmuse config init && openmuse doctor
openmuse serve # then open the QR code / link on your phoneDocker: ghcr.io/openmuseagent/openmuse:0.2.0 (amd64, arm64; -browser tag bundles Chromium).
The README opens with a demo — one task on a phone, start to finish. demo/mobilegym runs OpenMuse inside a simulated Android phone.
Full changelog: v0.1.0...v0.2.0