Paste this into your coding agent
muse: this guide as a working app
Everything on this page, runnable: onboarding with a name, look and tone, streaming chat with side chats and a send queue, Ideas, Goals, Library, a memory editor, reminders, connectors, and in-app notifications. Express plus vanilla JS, no build step. Clone it, add your key,
npm start.Your agent already has Muse’s shape
Muse keeps who you are and who it is in plain files, the layout OpenClaw made popular. Hermes, the agent on the defaultagent37-hermes template, uses the same idea:
The rest of the product is files too. The agent writes
~/muse/ideas.json, ~/muse/goals.json, and whatever it makes under ~/muse/library/, because its persona tells it to; your app reads them with the Files API and renders the tabs. The background work runs on crons, which wake a sleeping instance, so each user’s agent can sleep between tasks and bill disk alone.
1
Create the user's agent
One instance per user, created when they finish onboarding. Three fields make it a Muse: a monthly
budget (the managed LLM, search, and app calls are refused until you grant one), auto_sleep so an idle agent bills disk alone, and a random token in env that the agent later presents when it messages the user.monthly_cap_micros: 2000000 is a $2 monthly allowance per user, the counterpart of Muse’s weekly allowance; it resets each UTC month (see Budgets). env is write-only and fixed at create, so keep only the token’s hash on your side. The idle window of 30 minutes keeps the agent awake through a conversation and a long task; see long requests for why the timeout should outlast your slowest turn.The call returns 201 with status: "running" once the computer is up. Poll GET /v1/health on the instance URL until it answers "healthy": true before the first message (see Health & version).2
Give it a name and a personality
Write the persona into The block carries the name, tone, and timezone, the app’s files, and two rules the rest of this guide depends on:The follow-up rule matters: by default the agent is told it cannot follow up once a response ends, which is true of a plain chat turn but not of an agent that can schedule itself. The last sentence of the first paragraph is there because the stock file introduces the agent as Hermes Agent, and your block sits above that text.Hermes builds a session’s system prompt, SOUL.md included, on the session’s first turn and reuses it for every later turn of that session. A new name or tone therefore reaches new sessions (a new side chat, the next scheduled run) and not a conversation already under way. To make the change show up where the user is looking, save the persona, then move the main chat to a fresh session id and list the old one as an earlier chat. The avatar is your app’s own art; nothing on the instance needs it.
~/.hermes/SOUL.md. The file already has content, and the agent can edit it too, so your app owns one marked block and writes read-merge-write: read the file, replace what sits between the markers (or prepend the block the first time), and PUT the whole file back. A PUT replaces the entire file with the request body, so never send a fragment.the persona block (abridged)
3
Chat, side chats, and sending while it works
Muse has one main chat plus side chats for tangents. Each is a session on the user’s instance, and all of them share one memory. Mint the session ids in your app (32 hex characters) and store them: on Muse is not turn by turn: the user keeps typing while it works. A session runs one turn at a time, so hold messages typed during a turn in a client-side queue and send them together when the reply completes. When something else holds the session (a second tab, a page reloaded mid-turn), the post fails before the stream starts:Queue the message, follow the running turn with The card’s Stop button is
agent37-hermes an id the harness has not seen starts a new session under that id, and your own list stays complete, whereas GET /v1/sessions returns the 100 most recent sessions and every cron firing opens one.409 session_busy
GET /v1/responses/{response_id}/stream, and send the queue when it ends.The mascot’s status line and Muse’s Browser card come from the same stream. Map response.reasoning.delta to “Thinking…”, response.tool_call.started to a line per tool, and show a Browser card whenever the tool starts with browser_. On browser_navigate the label is the URL:stream
POST /v1/responses/{id}/cancel. The stock image’s browser is headless, so the card reports status; there is no live view to open.4
Ideas from a daily cron
Muse’s Ideas tab is a list of first-person proposals (“I can follow up on your airline refund”). Here a daily cron asks the agent to rewrite The Ideas tab reads the file with
~/muse/ideas.json from what it knows, and the app renders the file. Create the cron when the user finishes onboarding, and run it once right away so the tab is not empty on day one:GET /v1/files/content?path=~/muse/ideas.json, and shows “Updated” from the file’s modified in a GET /v1/files?path=~/muse listing rather than from the updated field the agent wrote: a model’s idea of the current time is not something to render. Tapping an idea sends its prompt as the next message in the main chat. A refresh button is the same run call, then a poll until modified changes; in testing a run landed in about 30 seconds. The daily cron is an ordinary cron that the user (from your Upcoming view) or the agent can delete, so when run answers 404, create it again. Once the user has talked to it for a while, the ideas stop being generic and start citing their memory and goals.5
Goals the agent checks in on
You do not build the goal tracker; the agent keeps it. When the user sets a goal (“run a 10K by December”) or asks it to watch something, the persona tells the agent to add an entry to A cron the agent created is an ordinary cron, so the app lists it next to its own and joins each goal to its cron for the real next check-in:Split them into Muse’s two sections by
~/muse/goals.json and schedule its own check-in with agent37 cron add, recording the cron id in the entry. The file after one such chat turn:~/muse/goals.json
kind: “Tracking” for things in the world (a price, a reservation) and “Goals” for things the user works toward. A “Check in now” button is POST /v1/instances/{id}/crons/{cron_id}/run. The same list, with pause (PATCH with enabled: false), run now, and delete, is Muse’s Upcoming view, and a reminder the user sets in your app is one more cron whose prompt tells the agent to message them.6
Let it message the user first
A scheduled run opens its own session and nobody is watching it, so its reply goes unread. The agent reaches the user by calling your server, with the token you planted in its With the persona’s rule in place, the whole loop runs without your app in it: the user says “remind me every weekday at 5pm to stretch”, the agent creates the cron itself, and at 5pm the cron wakes the instance, the agent runs
env at create. Your server writes a small script onto the instance once (and again whenever your public URL changes):~/muse/notify.mjs, written by your server with PUT /v1/files/content
AGENT37_INSTANCE_ID is set by the platform in every container, so the agent always knows which instance it is. Your endpoint checks the token against the hash you stored and records the message:node
notify.mjs, and the message is waiting in the app. Show it as a banner and in the main chat in time order, the way Muse delivers reminders into the conversation. A production app sends Web Push, email, or a text from this endpoint; the agent’s side does not change.7
Memory and Library through the Files API
Memory. Muse lets the user read and edit what it remembers. Show the entries of Send
USER.md and MEMORY.md, and make every edit a read-merge-write guarded by the file’s mtime, because the agent writes the same files mid-conversation. List ~/.hermes/memories for each file’s modified, read it, apply the one change to the current entries, and write back with X-Expected-Mtime:modified back exactly as the listing returned it, fractional part included. When the file does not exist yet, overwrite=false makes a create fail with 409 file_exists instead of clobbering one the agent just wrote. Keep entries under Hermes’ caps, by default 1,375 characters for USER.md and 2,200 for MEMORY.md, or the agent cannot add more until it consolidates. Like the persona, memory is read when a session starts, so an edit reaches the next session (a new side chat or the next scheduled run) and not a conversation already under way.For Muse’s “download your data”, export the two memory files and nothing else. Do not hand a user an archive of ~/.hermes: config.yaml there holds the instance’s managed-services token.Library. GET /v1/files?path=~/muse/library lists what the agent made, with size and modified for each entry; group by extension into Documents, Web artifacts, Images, and Audio. Preview a web artifact by fetching its text through your server and rendering it in <iframe sandbox="allow-scripts" srcdoc="...">: without allow-same-origin the page runs in an opaque origin and cannot touch your app. Let the browser ask for names under the library folder only, never raw paths, since your key can read every file on the instance.8
Connect apps
Muse’s Connectors screen maps onto app integrations, one Composio entity per instance:
GET /v1/instances/{id}/integrations/toolkits?search=gmail for the list, POST .../integrations/connect with a toolkit for an OAuth link, and GET .../integrations/connections until the new account reads ACTIVE. Open the link in a new tab from the click handler itself so popup blockers allow it, and pass an https:// callbackUrl to land the user on a “you can close this tab” page of your own. The agent can use a connected app from its next message, and the daily ideas get better with every app it can read.Message it from WhatsApp
Muse is also reachable in WhatsApp. Messaging channels connects a user’s agent to WhatsApp or Telegram from your own app, and Text your agent on iMessage gives it an iMessage line.Worth knowing
- Allowances are dollars per month.
monthly_cap_microsresets each UTC month; Muse’s weekly token allowance has no direct equivalent.GET /v1/instances/{id}/budgetreturnsmonthly_consumed_microsfor a usage meter. - What it costs per user. Asleep, a default instance bills its disk alone, about $0.36 per month; awake minutes bill the $4.76 monthly rate pro rata. Daily ideas, reminders, and check-ins each cost one agent turn plus the minutes it keeps the instance awake. How many users you can serve is capped by your instance limit.
- Background work and sleep. A turn keeps running after the user closes the app, but with
auto_sleepidle is measured in bytes through the instance’s URLs, so keepidle_timeout_secondsabove your longest task. - Status, not a live view. The Browser card narrates tool events. Watching or taking over the browser needs a desktop image.
- Purchases hand back. The agent can research and fill a cart; the user checks out.
- Meta’s own connectors (Instagram, Facebook, Messenger) have no equivalent; the catalog is Composio’s.
- Reset is delete. Muse’s Reset maps to
DELETE /v1/instances/{id}and a fresh create. Delete is destructive: files, memory, and sessions go with the instance.