Changelog
All notable changes to JoaxClaw are documented here. Format follows Keep a Changelog.
[Unreleased]
[0.27.1] - 2026-09-19
Fixed
- Screen readers now announce disabled buttons as unavailable. App-wide, buttons that can't be used yet, such as Join and take notes in Meetings or a button that is busy saving, were announced as ordinary working buttons. They're now marked as unavailable (and busy while working), still ignore clicks and Enter or Space, and stay focusable so their tooltip explaining why still shows.
- You can export a chat while it's still running. Exporting or copying a running chat longer than one page always failed with "This chat changed while it was being read", because the reply kept adding messages during the read. The export now saves the chat as it was when the read started and marks it as still running.
- No more empty "Assistant" bubble under a reply. After a normal send, an empty reply bubble could stay under the answer. The gateway briefly reports a finished run as still running, and the chat attached to it again. The chat now ignores that report, and a reply bubble that ends with nothing in it is removed.
- The plugin list and the plugin's details agree on what it needs. After installing DuckDuckGo, Extensions said "Needs key" while its detail pane said "Nothing to configure". DuckDuckGo needs no key. The list guessed from a built-in table, and on OpenClaw 2026.9.4 it now uses the gateway's own word on whether a plugin needs setup, the same one the catalog shows. A plugin the gateway says still needs configuring is marked Needs setup. Older gateways keep the previous check, with DuckDuckGo corrected.
- Meetings no longer says notes are off right after you turn them on. Turning meeting notes on restarts the gateway, and for a couple of seconds after it came back the setup checklist showed the old "Meeting notes are off". It now says "Turning on meeting notes…" and then "Meeting notes are on" through the restart, and after any reconnect it shows "Checking meeting notes…" until the gateway has answered, never an out-of-date answer.
- A slow gateway update is no longer reported as refused. An update from a source checkout that took longer than 15 minutes showed "The gateway didn't update" even though it was still running. JoaxClaw now keeps showing Still updating, finds the update in the gateway's history, follows it to the end, and tells you how it ended. On gateways older than 2026.9, which keep no update history, it says the update may still be running and to check the Gateway tab, and reports the version the gateway comes back on.
[0.27.0] - 2026-09-18
Added
- Gateway updates from the app, for every connection. The update notice used to reach only connections with admin rights; now every connection sees it as soon as it connects. On an admin connection, Update gateway asks once, then has the gateway update itself directly, with no agent chat in between. The banner follows each phase (staging, validating, activating, restarting, verifying), the reconnect screen says the gateway is updating and keeps trying for as long as an update can take, and afterwards you're told once whether it worked, failed or was rolled back. If the gateway refuses (restarts turned off, an outside supervisor, a package install it can't update itself), the banner says why and what to do instead. Without admin, Update via agent works as before, and a read-only connection gets the command to run on the host.
- Automatic updates and update history. When the gateway is about to update itself, the banner shows the countdown; an admin can Hold 1 hour once or Update now. Gateway → Gateway → History lists the last 20 updates (versions, who started them, how they ended, downtime), with per-phase Details, Copy failure details, and, where the gateway allows it, Report on GitHub.
- Background work in the chat. A Background strip under the chat header shows everything the chat has running: sub-agents, background commands, automation runs, media jobs and scripts, for example "2 running · 1 needs you · 3 done". Each row can be stopped, and a result that couldn't be delivered back to the chat offers Retry delivery or Dismiss. Clicking a task opens a drawer with its live output, prompt and result; a list view filters every agent's work. The Dashboard's Background section, the desktop tray and "task failed" push notifications (which open the task's chat with the task beside it) cover the same work. On a phone the strip is a chip that opens a sheet. Gateway tasks need OpenClaw 2026.7 or later; scripts show on any gateway.
- Chat goals. On OpenClaw 2026.8 and later, Set goal in the chat header gives a chat an objective the agent keeps working toward on every turn. The button becomes a chip showing where the goal stands (Pursuing, Paused, Blocked, Done). From it you can pause, resume with a note, continue a blocked goal with instructions, keep going past a used-up budget, edit, mark done or clear. Chats with a goal get a marker in the chat list, and Sessions has a Goal column. Retry after a dropped connection resends the same request, so an action is never applied twice.
- Export a chat. The chat header's new ⋯ menu (and each chat's row) saves the whole conversation, read in full from the gateway, as Markdown with each tool call folded under its own heading, or as JSON exactly as the gateway sent it. It says if long messages were shortened or the chat was still running. Copy as Markdown puts the same text on the clipboard. Model text in the export can't carry live HTML or script links.
- Browse plugins like a store. On OpenClaw 2026.9, Extensions → Plugins → Browse opens the gateway's catalog: Featured, Trending, Official, Bundled and All, by category, with each plugin's state on your gateway and a detail pane (publisher, what it needs, ClawHub's security status, version, README). Without admin you get the host command or can ask an agent to install it. Older gateways keep the search box.
- Meetings. A new Meetings section lists every meeting your gateway took notes in (Google Meet, Teams, Zoom, Discord), live ones first. Read the summary and the searchable transcript, download it as Markdown or JSONL, ask the owning agent for a summary, or start a chat about it. Record a meeting sends an agent into a Meet, Teams or Zoom call to take notes, shows how the join is going (lobby, sign-in, recording) and has Stop; it asks you to confirm you'll tell participants, every time. A setup checklist turns meeting notes on, installs the meeting plugin you need and runs the Chrome check on the host. These are notes from the meeting's captions and a summary, not audio or video recordings. Needs OpenClaw 2026.8 or later.
Changed
- Installing a plugin restarts the gateway for you. Installing from Browse or from the Add a channel form used to end with "Restart the gateway for these changes to take effect". JoaxClaw now restarts it, shows "Restarting to finish installing {name}" while it's away, waits for it, and tells you whether the plugin is ready, needs setup, or failed to load. If the gateway is busy it restarts once the work finishes, and says so. When the install policy flags something in a package, you see what it found and can choose to continue.
- Scripts moved into the Background strip. The separate Scripts strip and the Dashboard's Scripts section are gone; scripts show with the rest of the chat's background work. A running script is now the accent colour instead of amber, which is reserved for things that need you.
- Running chats are marked in the accent colour. A chat that's working (the Active group in the chat list, its pulsing dot, and "running" in Sessions) used to be green, the colour the app otherwise uses for "done". Working is now the accent colour everywhere, and green means finished. Pulsing dots also stop pulsing if your system asks for reduced motion.
- Dialogs are easier to use from the keyboard. The Channels dialogs keep focus inside while open and return it when closed, and screen readers announce them by their title. The phone bottom sheets (More, System monitor) close with Escape.
Fixed
- Official channel plugins can be installed from the app on OpenClaw 2026.9. The one-click install for a channel's plugin (Slack and the other official channels) always failed there with "unknown official plugin catalog entry", because the gateway lists them by package name. It installs by package name now.
- Reopened chats show a sub-agent's tool calls too. Tool calls saved in other formats, and the calls a sub-agent made while the main agent waited for it, were still missing after reopening a chat. They now show, a chat that's still working shows its latest calls as running until the run finishes, and a long chat that doesn't fully load says so at the top.
- The web and phone app no longer goes blank when a reply starts. In the browser build (the PWA), the page went blank as soon as a reply began streaming and stayed that way until reloaded. A part of the app written for the desktop app's local-model progress bar expected something the browser build didn't provide, and the error took the whole page down with it. The desktop app wasn't affected.
- Tool calls no longer disappear when you reopen a chat. When you reopened a chat or restarted the app, every tool call in it was gone, and so were the diffs, question cards, script cards and sub-agent tool lists built from them, even though they'd shown fine while the reply was streaming. The gateway keeps them. The app expected a different format from the one the gateway uses, and threw away the separate entries that carry each call's result. Both are read now, so a reopened chat shows each call with its arguments and its result or error, the same as it did live. Reopening a long chat also reads its last 500 messages instead of 200.
- Agents that start a long script now check back for the result. Long scripts run as background jobs (
script_start). Agents were told, by JoaxClaw'sscript-runnerskill and by the tool's own reply, to end their turn because the session would be woken with the result when the script finished. That wake-up never happens for a plugin installed from npm, as joaxclaw-fs is. So an agent that followed the instructions simply stopped, and the result went unseen until someone asked again. (0.26.0 corrected the tool's description but missed these two.) Both now say the opposite: check on the job withscript_statusuntil it's done, and give the user the job id if it will run for a long time. The skill updates itself on the next connect. The plugin is 0.14.1; update it on the gateway withopenclaw plugins update joaxclaw-fs.
[0.26.1] - 2026-09-15
Fixed
- Slack and WhatsApp conversations update live again. Messages that came through a channel (someone writing to your agent in Slack or WhatsApp, and its reply there) often didn't show up in JoaxClaw until the app was reloaded. Two things were wrong. The app asked the gateway for live session updates once per run, but the gateway ties that request to a single connection, so after the first reconnect (the laptop sleeping, a network blip, a gateway restart) the updates silently stopped. And even while updates were arriving, the app ignored the one that carries new messages. It now renews the request on every connection, and adds new messages to an open conversation as they arrive, without switching away from the chat you're in.
- Adding Slack, and most other channels, works on OpenClaw 2026.9. On 2026.9, almost every channel (Slack, Discord, WhatsApp, Signal, Microsoft Teams and more; Telegram is the exception) is its own plugin, which has to be installed before the channel can do anything. The Add a channel form didn't know that. It took your keys and saved them, and with no plugin to use them nothing ever connected. The form now checks whether the channel's plugin is on your gateway. When it isn't, the form installs it in one click (for official plugins, on an admin connection) or shows the command to run on the gateway host. Slack also gets a choice between Socket Mode and an HTTP Request URL. Each mode asks only for the keys it uses (the form used to demand an app-level token even for HTTP, which doesn't have one), and the choice is saved. A bot token pasted where the app-level token goes, or the other way round, is now caught in the form.
[0.26.0] - 2026-09-15
Added
- A way out when a local model writes its tool calls as text. On OpenClaw 2026.9, local models get Tool Search by default. Instead of their tools they get a search tool and a list of tool names, and models such as Qwen tend to write the calls into their reply, so nothing runs. When a local model does that, the Written as text notice now names the likely cause and offers to turn Tool Search off for the gateway in one click. If your connection can't change gateway settings, it shows the command to run instead. The change applies straight away and doesn't affect cloud models.
- Your agent's plan, above the chat. On OpenClaw 2026.8 and later, agents keep a progress card for longer jobs: a checklist of steps, a short status note, or both, updated as the work moves on. OpenClaw's own apps show it beside the session. JoaxClaw showed nothing, so models took to writing progress bars into their replies instead. The card now sits under the context bar as a single line: the step being worked on, a progress bar, and a count such as 2/3. Click it to open the full plan and the note. It updates live while the agent works, and it's still there when you reopen a chat. It uses the same bar as the context meter, and the progress bars that appear in replies now use it too, so all three look alike. Older gateways don't have progress cards, so nothing changes there.
Changed
- Teams is one page now, not five tabs. A team had Build, Graph, Monitor, Docs and History, and nothing told you which to open. Two of them edited the same team through different representations — Build changed the blueprint, Graph changed the compiled output of the blueprint — so the two could disagree; the tab's own tooltip warned that they would, and the file format carried a flag to record when they had. Meanwhile the thing you do most, running the team against a task, sat buried inside the editing form. The team is now shown as what it is: a numbered sequence of steps, each with its role, the agent that plays it and what it does. The task box and Run are the first thing on the page. A run reports its progress on that same sequence — the step that is working, the ones behind it, the one that failed and why — instead of in a separate tab with its own vocabulary. Branching is drawn on the step it belongs to, so a team that doesn't branch shows nothing about branching, and a review gate appears as a pause on the approach to the step it guards. Runs and saved versions moved to a rail beside it. The Graph editor is gone from Teams: the sequence is the team, the executable flow is generated from it, and a second editable copy could only drift — none of the teams this was built against had ever used it. It remains in Processes, which is where it belongs. The separate phone screen is gone too, so there is one layout to change instead of two that quietly diverged.
Fixed
- Windows redraw after the machine wakes. Left open overnight, a JoaxClaw window could come back from sleep or hibernation as a black rectangle: the app was still running and the gateway still connected, but the window never drew itself again, and only restarting the app brought it back. The app now asks every window to redraw shortly after the machine wakes, and again if the graphics process restarts. It also recovers from two related failures that used to leave a window blank for good: a window whose page crashes reloads itself (a popped-out chat or file comes back to the same one), and a page that fails to load retries a few times instead of giving up on the first attempt.
- Switching a chat's thinking level back to Adaptive now takes effect. The gateway saves a level picked in the chat header on the session, so choosing Adaptive afterwards changed nothing: the old level stayed in force. Adaptive now clears it. The menu also offers only the levels the chat's model accepts. Ollama models take Off to Max but not Minimal or Extra High, and sending either of those failed the whole message. A level left over from a previous model is no longer sent.
- Collapsible sections and progress bars in replies now render. Models sometimes write a little HTML where markdown has no equivalent — a
<details>block to fold away a plan, a<progress>bar for "Automated monitoring · 2/3" — and the chat showed the raw tags. These two are now drawn properly: the plan folds under its summary line, with its steps still formatted, and the progress bar shows its label and count. The same applies to Markdown files opened in the Files drawer. Only these tags are recognised, and only the attributes that matter (value,max, the label,open) are read. Every other bit of HTML is still shown as text rather than run, so a reply can't slip a script or an event handler into the app. Tags inside a code block are left alone, and a block that is still streaming stays hidden until it is complete, instead of flashing up as half-written markup.
- A team started without the app now leaves a trace. Run history was written only by JoaxClaw, so a team an agent started from Slack or a schedule was invisible afterwards: the Teams screen said never run, however many times it had. The gateway now records the launch itself, the moment it hands out a team's prompt — with the task it was given, so you can see what was asked for. It is recorded honestly as a launch requested, not a run completed: an agent can ask for a prompt and never start the team, and the screen says so rather than claiming steps that never ran. When JoaxClaw next connects it adopts the real run and takes over the record. Asking for a prompt while a team is mid-run no longer disturbs the run in progress.
- An agent can start a team on its own now. Ask an agent on Slack, or a scheduled automation, to run one of your teams and — until now — nothing happened. The skill told agents to call
teams.run, and told them "the desktop app picks up the request, runs the team": that call only wrote a request file and returnedok, so unless JoaxClaw happened to be open and watching, the team never started and the agent had no way to know. There is a stranded request of exactly that kind sitting on the gateway this was found on. The gateway can now hand out everything needed to start a team —teams.launchPromptreturns the finished Team Lead prompt and the agent it must be given to — and the caller starts it with the ordinary spawn tool every agent already has. The run then proceeds on the gateway like any other, and carries a label the app recognises, so opening JoaxClaw later adopts the run and shows its progress.teams.runkeeps its old meaning for the case it suits: queueing a task for you to review and start yourself.
script_startno longer promises a wake-up it can't deliver. The tool told agents that when a long-running script finished, "THIS session is automatically woken with the result — so you do NOT need to poll or block: launch it, then continue other work or end your turn". That wake has never fired. The scheduler behind it is offered to every plugin, but the gateway drops the call unless the plugin ships bundled inside OpenClaw itself; installed from npm, as this one is, it returns without warning and without scheduling anything. So an agent that took the tool at its word ended its turn and the result never arrived — the failure being silence, which is the hardest kind to notice. The tool now tells agents to pollscript_status, and says why. The wake attempt stays in the code because it costs nothing and would work if the plugin were ever bundled; it simply isn't advertised any more.
[0.25.0] - 2026-09-10
Added
- Find and update skills without leaving the app. Extensions could list the skills already configured and switch them on, and the app installs its own bundled ones — but discovering somebody else's skill, or refreshing one already installed, meant a shell. Browse now searches ClawHub from the Skills tab and installs from the results, showing each one's publisher, whether it's official, its summary and install count. Update installed refreshes everything tracked from ClawHub in one go. That last one has a trap the gateway is careful about and so is this: a skill whose files you've edited since installing is refused rather than quietly overwritten, and the app names those skills and asks, rather than retrying with force behind your back. There is a subtler trap in installing, too. A search result carries both a
slugand a publisher-qualifiedinstallRef, and several publishers can share one slug — so installing anything by its bare slug risks fetching a different author's skill under the name you searched for. Only the qualified reference is ever sent, and it's the one shown on each row. Finally, a result whose name clashes with one of JoaxClaw's own skills —ask-user,script-runner,teams-blueprint,process-builder— is flagged rather than offered, because every skill installs to the same place regardless of who published it: installing it would replace the app's copy, and the app would replace it back on the next connect. Nothing on ClawHub uses those names today; this is so it stays uneventful if something ever does.
- Install plugins without leaving the app. The Extensions page could list plugins and switch them on, and that was all — "Add plugin" registers a config entry for something already on disk, so actually getting a plugin onto your gateway meant a shell. Browse now searches ClawHub and installs from the results, showing each package's provenance (official, community or private), its summary, version and download count. Installing surfaces the gateway's consent step rather than skipping past it: a plugin declares what it will register — messaging channels, agent tools, MCP servers, CLI commands, and any config flags its own manifest marks dangerous — and the gateway refuses until that has been acknowledged. The app shows you the list and asks; the token that records your agreement is only ever sent after you have seen what it covers. If a plugin you already approved has grown its reach since, the prompt says so and shows only what is new, which is the case the mechanism exists for.
Fixed
- Reconnecting works again. In 0.24.0 and 0.24.1, once a connection dropped — a laptop waking from sleep, a network change, a gateway restart — the app could not get back, failing every attempt with "Auth rejected: unknown method: connect" until it was restarted. The rejection never came from the gateway: the app had started skipping methods the gateway doesn't advertise, and the handshake itself is never advertised, while the list from the previous connection was never cleared. So the first connection worked and every one after it refused itself. Scope renegotiation and the retries for a stale device token or a gateway still starting up reopen the same way, and were broken too. Each connection now starts with a clean slate, and the handshake is never skipped.
- A tool call the model writes out as text now says that nothing ran. Models sometimes put a call into their reply instead of making it —
<ls><path>/home</path></ls>, a<tool_call>{…}</tool_call>, a<read-files>list copied from the gateway's own compaction notes — and the chat showed the raw XML, after something like "Let me check the vault…", with nothing having been checked. No gateway executes these. They now come out of the prose and show as Written as text — nothing was run, with a plain summary of each attempt and its details on request. This also covers<bash>,<edit>and similar tags, which the chat used to draw as action pills with a lightning icon, as if they had run; a text<edit>keeps its diff, labelled as a proposal that was not applied.
[0.24.1] - 2026-09-09
Added
- Devices show whether they're actually online. The gateway has always reported
connected,lastSeenAtMsandlastSeenReasonfor every paired device, and the Devices panel rendered none of them — a phone connected right now looked identical to one last seen in April, since the only timestamp shown was when it was first paired. Each device now carries a live dot and reads online or seen 3h, with the exact time and the reason it was last seen (signed in, woken by a push, a background refresh) on hover. Devices can also be renamed, so a list ofopenclaw-control-uiandios-nodeentries can become "my phone" and "work laptop"; the name is stored on the gateway against the stable device id, so it follows the device. - Nodes report their host's resources. OpenClaw 2026.9 added a resource snapshot that capability nodes — a phone, a CLI host — send on connect and every 60 seconds. A Nodes section now lists them with their capabilities and meters for load, memory and disk. Two details it is careful about: load is scaled by core count, because a load of 4 on an 8-core host is half busy rather than four times over; and a value the host didn't report shows a dash rather than an empty bar, which would read as "nothing in use". The gateway keeps serving a node's last snapshot while it is offline, so a reading older than five minutes is labelled with its age instead of being presented as current. The section is hidden entirely when the gateway has no nodes, which is the common case.
Fixed
- The app now listens to what the gateway says about itself. Every connection answers the handshake with a description of that exact gateway — the methods it implements, the attachment sizes it accepts, the frame it can hold — and all of it was thrown away. Six consequences, now fixed. Method support was learned by calling a method and pattern-matching the rejection, so every unsupported RPC cost a wasted request and an error in your gateway's log on each fresh connection; the advertised list is consulted first, while still treating "not advertised" as try it, because the gateway documents that list as deliberately incomplete. An oversized attachment was discovered by the gateway refusing the whole send; the composer now says which file is too big and what the limit is before anything is uploaded, including the case where each file fits but the base64-encoded batch overflows the frame. A scope refusal was read by matching a message the gateway calls changeable, rather than the structured details naming the exact missing scope. A failed run threw away
errorDetail— the provider, model, HTTP status and a redacted reason — and showed a guess about watchdogs instead. A gateway still finishing startup answers "not yet, retry"; that was treated as a terminal authentication failure. And the session list took two round-trips wheresessions.subscribereturns the subscription and the first page together. - The approvals banner could never have shown anything. Approvals shipped in 0.24.0 with a call the gateway rejects: the pending lists carry only a raw request, so each one is looked up with
approval.getto fetch the reviewer-safe description — and that method takes the approval id and nothing else. The app also sent thekind, which its siblingapproval.resolvedoes require, and against a closed schema an extra property fails the whole call. Because the lookup step treats a failure as "nothing to show", the result was silent: no error, no banner, just an agent still waiting. Caught by raising a real approval on a live gateway and watching the call come backinvalid approval.get params. Every other method added in 0.24.0 was re-checked against its schema the same way; this was the only one wrong.
[0.24.0] - 2026-09-09
Added
- Search inside your conversations, not just their titles. The Sessions filter box only ever matched session names and keys, and only among the sessions already loaded — so finding the conversation where something was actually said meant opening them one by one. Typing two or more characters now offers Search messages, which runs the gateway's full-text transcript search and lists the matching messages grouped by conversation, with the sender and time; clicking one opens that session in Chat. It stays a deliberate button rather than firing as you type, because unlike the name filter beside it every run reads transcripts on the gateway. Two things the gateway's shape forced, both found by probing it: the search is scoped to an explicit list of sessions rather than being global — omitting the list makes the gateway fall back to a session key called
mainand fail outright on a multi-agent setup — and it accepts at most 200 of them, so the newest 200 are searched and the results say plainly when older ones were left out.
- Notifications now reach your phone with the app closed. The PWA could only alert you while it was still in memory — switch away long enough for the OS to reclaim it and a finished run went unannounced. True background push was blocked on the gateway side; OpenClaw 2026.9 unblocked it, adding
push.web.and keeping its own VAPID signing key and browser subscriptions instate/openclaw.sqlite, with no config key to set up. Settings → Notifications gains "Also notify me when the app is closed"*, which registers this browser with your gateway — it works because the PWA already pairs as its own device, which is exactly whatpush.web.subscriberequires. A fresh subscription starts on the gateway's defaults, which enable approval requests and nothing else; JoaxClaw has no approvals view yet, so it opts itself into the four categories it can actually act on instead — agent finished, agent has a question, automation failed, background task failed — each toggleable, with a Send a test — plus approval needed, once that banner landed. An agent parked waiting on your answer or your sign-off is the case most worth waking a phone for, since nothing proceeds until you reply. Only human mentions stay off, having no surface here at all, since a notification that opens onto nothing is worse than no notification. Tapping one routes in-app: the gateway sends its own Control UI path, so a failed automation opens Automations and a question opens the chat, and anything we have no view for simply focuses the app rather than opening the gateway's own UI over the top of this one.
Fixed
- The type checker was never actually running.
npm run type-check— also part of the pre-commit hook — pointed at a roottsconfig.jsonthat is a solution file:"files": []plus two project references. Plaintsc --noEmitagainst that traverses nothing and exits 0 every time, so the gate has been passing regardless of the code. Pointed at the real projects it reported 59 errors, all of which are now fixed, and the script checks both of them properly. Four were live bugs rather than typing noise: the Dashboard's "sessions active" chip and Sessions tile navigated nowhere, because Sessions is a tab inside Gateway rather than a section of its own; a team that fails validation showed a generic "not runnable" message instead of the reasons, since the code read areasonfield that never existed on the result; GPU memory always read as zero, because the fields were namedmemUsed/memTotalwheresysteminformationcalls themmemoryUsed/memoryTotal; and an Obsidian vault always displayed as "remote", local or not, because the vault reference had nomodeto read. The rest were dead comparisons, unused imports, and typings that had drifted — includingBtn, which never declared thetitleprop or the icon-only form that a dozen call sites already used.
- Your profile and saved connections survive on the phone now. The durable backup added in 0.23.2 only ever worked on the desktop: it writes
~/.joaxclaw/store.jsonthrough an Electron IPC bridge, and in the browser build that bridge doesn't exist — sowriteLocalStorereturned immediately and every write was silently a no-op. The PWA kept your "About You" profile and your saved gateway connections inlocalStoragealone, which is the storage mobile browsers evict first: Android Chrome discards it under storage pressure, and iOS Safari clears it after seven days without a visit unless the app is installed to the Home Screen. The same class of loss the desktop backup was written to prevent, on the surface where it was most likely. The browser build now mirrors the same data to IndexedDB, using the identical shape and merge rules, so nothing above that layer changed. It also asks the browser for persistent storage at startup, which is what actually stops eviction — Chrome grants it automatically to an installed PWA, Safari weighs it by how much you use the app, and a refusal simply leaves things as they were. Under Electron the file still wins, because the packaged app (file://) and a dev build (http://localhost:5173) are separate origins with separate IndexedDB databases, and falling back there would quietly reopen the cross-origin split the file was added to close. - An agent blocked on an approval no longer just stalls. When policy says a command or a plugin tool needs sign-off, the gateway parks the run and broadcasts an approval request. JoaxClaw asked for the
operator.approvalsscope at connect and then never used it — so from the app's side the run simply stopped, with nothing to say why or any way to respond; the only way through was the gateway's own Control UI or a channel. Pending approvals now appear in a banner at the top of the app, with the buttons the request itself permits. All three kinds are covered — a shell command (exec), a plugin tool (plugin), and a change to OpenClaw itself (system-agent) — listed on connect so anything raised while the app was closed is caught, and kept live by the.approval.requested/.resolvedbroadcasts, so answering from your phone or the Control UI clears it here too. Two details worth knowing: the buttons come from the request's ownallowedDecisionsrather than being hardcoded, because they differ by kind (a system-agent change offers Allow once and Deny but never Always allow*), and the banner shows the gateway's reviewer-safe description — fetched separately from the pending list, which carries the raw request — so the runtime working directory, environment and execution plan stay out of the UI by design. The banner is deliberately not dismissible: hiding it would put the run back into exactly the silent stall this fixes.
- The agent Files tab no longer pretends to be a folder. "New file" and the delete button both looked like they worked and neither did: the file list came back unchanged the moment you reopened the tab.
agents.files.*is not a folder API — the gateway resolves every call through a fixed allowlist of six core workspace files (AGENTS.md,SOUL.md,IDENTITY.md,USER.md,BOOTSTRAP.md,MEMORY.md) and refuses any other name withunsupported file "<name>", while delete has no RPC at all:list,getandsetare the entire surface, andsetonly ever writes. Both handlers swallowed the error and updated the list optimistically, which is why the failure was invisible. Add file is now a picker of the core files this agent is missing — the gateway namesSOUL.md,IDENTITY.md,USER.mdandMEMORY.mdas the ones whose absence is normal and asks editors to offer exactly those — and it reports a refusal instead of hiding it. The delete button is gone, since nothing in the protocol can carry out what it promised. Verified against the shipped OpenClaw 2026.9.3 handlers; every other method the app calls is still registered there. - Talk no longer goes silent the moment the voice needs your agent. Ask anything the realtime voice can't answer by itself and the call froze on "Working…" — the orb kept listening, the agent never spoke, and only hanging up recovered it.
brain: agent-consultturns out not to mean "the gateway answers for you": the relay hands the voice provider anopenclaw_agent_consulttool, and when the provider calls it the gateway parks the provider on a "working" placeholder, forwards the tool call to the client that owns the session, and waits for that client to run the consult and submit the result. JoaxClaw only drew the tool call in its activity feed, so the provider waited forever. It now runs the round-trip the gateway (and its own control UI) expects:talk.client.toolCallstarts the agent run, the run's reply is read off thechatevent stream, andtalk.session.submitToolResulthands it back so the voice speaks it — with the agent's tool steps shown live on the activity row. Two things had to change with it: the Talk session is now created with an explicit session key (agent:<id>:main, ormainfor the default agent), because the gateway never reveals which key it picked and rejects the consult if the client sends a different one; andopenclaw_agent_control("how's it going?", "stop that") is answered too, viatalk.session.steer. A consult that fails or times out now returns an error the voice can say out loud instead of silence, and hanging up mid-consult aborts the agent run it started. Protocol notes in src/lib/TALK.md. - Talk can now be told to always ask your agent. There is a second way the voice went quiet, with a different fingerprint: an empty agent-activity feed, because the realtime model never called the consult tool at all — it just said "Let me check that for you" and stopped. Nothing obliges it to use the tool, and the gateway's fallback for that is configuration rather than client code. Talk's settings now carry "Always ask the agent" (
talk.realtime.consultRouting): off, the voice model decides for itself whether to consult your agent; on, every finished spoken turn is routed to the agent and the voice speaks its answer. The header says which mode you're in, so a call that answers suspiciously fast is no longer a mystery. - Answering an agent's question actually answers it now. When an agent asks something through the gateway's
ask_usertool, the gateway opens a question record and parks the run on it — the run is waiting forquestion.resolve, not for your next message. JoaxClaw only ever answered by sending the chosen label as an ordinary chat turn, which is precisely the path OpenClaw 2026.9's "recover lost answer confirmations without sending the same input through ordinary steering again" fix describes: the parked run isn't listening to the transcript, so the answer could go nowhere while the card looked answered. Questions the gateway is holding for the open chat now appear pinned above the composer — a parked run isn't something to scroll back for — and clicking an option resolves the record properly. A record with several questions (the gateway allows up to three) holds until all of them are answered, because it resolves in one call. Answering from somewhere else, like the gateway's own Control UI or another device, clears the card here rather than leaving it clickable. This needed a new scope:operator.questions, which — unlikeoperator.talk— is not implied byoperator.write, so every one of these calls was previously rejected outright. The in-transcript<ask>card is untouched: nothing is parked on those, and chatting the answer back is the whole mechanism. Questions asking for a secret are deliberately left to the gateway's own UI, since this card renders options as buttons and has no masked input.
[0.23.2] - 2026-09-03
Fixed
- The welcome no longer asks who you are every time you open the app. Your "About You" profile and the fact you had answered the first-run welcome were kept only in
localStorage, which is not durable in an Electron renderer — the same weakness that lost a set of saved gateway connections once, and why those are already mirrored to~/.joaxclaw/store.json. The profile now gets the same durable backup. It also fixes a second cause with no corruption involved: the packaged app (file://) and a local dev build (http://localhost:5173) are separatelocalStorageorigins, so a profile entered in one was invisible to the other. Your local answer always wins over the backup, and "already answered" is one-way — a stale backup can never make the welcome reappear. Existing installs are asked once more, since a wipedlocalStoragehas nothing to restore from; that answer is what seeds the backup.
[0.23.1] - 2026-09-03
Fixed
- A finished answer no longer leaves the chat stuck on "thinking…". When the model completed a turn, an empty assistant bubble appeared beneath the answer and kept spinning, with the composer blocked — the only way out was to press Stop or wait 60 seconds for the stall detector. The gateway was never the cause: with a chat in this state it reports every session idle and the relevant ones
done.sessions.listlags the final frame by a beat, so the pane briefly read the session as still running and re-attached to it; because a completed answer is not a reusable turn, that appended a fresh empty placeholder waiting for output that was never coming. The app now refuses to re-attach to a session the gateway reports idle, so the placeholder is never created, and a reconciler settles any turn left streaming after its run ends. Asessions_yield— where the gateway ends a run, auto-resumes, and answers later — is deliberately exempt, since a controller reads idle for a moment before its worker registers.
[0.23.0] - 2026-09-02
Fixed
- Works with OpenClaw 2026.8 again. The gateway's 2026.8 upgrade renamed config keys and RPC parameters the app was still sending, and the new versions reject the old names outright rather than tolerating them — so saving subagents, toggling an automation, and skill descriptions were all broken against an upgraded gateway.
agents.listbecameagents.entries(a map keyed by agent id, not an array),cron.updatetakesidrather thanjobId, andskills.statusnow refuses an unscoped call when several agents are configured. Reads still accept either shape, so a gateway you haven't upgraded keeps working. All 73 methods the app calls were probed against a live 2026.8.2 gateway to confirm this was shape drift rather than removals. Two gateway-side settings are still worth checking yourself if you hit them:talk.agentIdandagents.defaults.systemAgent.agentIdbeing unset break Talk and agent-less automations respectively. - Reconnecting no longer piles up blank turns. The live-run re-attach added in 0.22.0 could stack empty assistant bubbles and leave the composer stuck on "model working" — reported as the hosted web app freezing to a blank screen. A dropped socket clears the streaming flag while the session stays active, so the pane re-attached on every blip and appended another placeholder, each one blocking the input until the stall detector freed it. On a flaky connection it repeated indefinitely.
- Loaded models are reported per engine. A gateway commonly runs a second, isolated Ollama for background work, which loads its own copy of a model into the same GPU. Five surfaces read a flattened view of the interactive instance only, so anything resident on the background one was invisible — and a compounding bug in the two model pickers made it unreachable as well.
Changed
- "Crons" are now "Automations", matching the wording the gateway itself moved to — its 2026.8 errors read "Automation not found". The nav rail, mobile nav, view header, empty state, dashboard section, the Models usage column, the chat list's scheduled-run tooltip and the Help copy all follow. The cron expression keeps its name, because
0 9 *is the name of the syntax rather than of the feature. - The Automations list shows the ones you created. A live 2026.8 gateway returns 21 jobs, 13 of them generated by the gateway itself — a skill-collection review per agent, per-agent heartbeats, memory promotion — which outnumbered the 8 real ones in both the list and the dashboard. Generated jobs are identified by the marker the gateway sets on them, and hidden.
Added
- Syntax highlighting in the file viewer. Code opened from the Files drawer was flat monospace; it now has highlighting and line numbers, so a file reads the same whether it arrives as a diff in chat or as a file here. No new dependency — it reuses the tokenizer the diff view already uses. Highlighting is skipped above 5,000 lines, where the file is usually a log and the cost outweighs the colour.
[0.22.0] - 2026-08-19
Added
- Billing — what you're actually spending, priced with your own rates. A new Billing section (nav rail, and the mobile More sheet) answers "what are we spending per hour / day / week". Tokens come from the gateway's usage report, which reads every session transcript and buckets by day, model and agent — a real history rather than the last-run counters the Models → Usage tab aggregates. Dollars are recomputed from the prices you set on the Models page, because the gateway's built-in rates are mostly unset: its own estimate priced a 125M-token week at $0.000018. That figure is still shown, as a footnote, so the two can be compared.
Fixed
- Stop works on a run that's still going. If a run outlived its stream — the turn finalized while the agent kept working, or the socket dropped mid-run — the chat list showed a live dot next to a transcript that looked finished, with an empty assistant bubble and no way to intervene. Stop was a no-op in exactly that state. The list and the transcript now answer "is this running?" from one shared source; when the session is live but nothing is streaming here, the thread says so, offers a Stop that actually aborts, and re-attaches to the run so new output arrives.
- "Unknown error" now names the likely cause. The gateway can end a run with an error carrying no message at all, which rendered as a bare "⚠ Unknown error" pointing nowhere. In practice it's almost always the LLM idle watchdog killing a slow local model — the turn now says so and sends you to the gateway log, where the real line is.
- The System Monitor reports the machine your agents actually run on. GPU could vanish entirely on a local gateway (the client-side probe only understands nvidia-smi, so an AMD box reported nothing) — host metrics are now used on every tick, local included, so one path serves both and they can't disagree. "Models in VRAM" also read a hardcoded default engine, missing the second isolated instance a gateway commonly runs for cron work: residency could read as half of reality while the meters above it moved for no visible reason. It's now listed per engine, and on a remote gateway it reads the host's engines rather than yours.
- RAM and GPU appear on a remote gateway. The status bar hid both meters whenever the gateway was remote — a guard from before the app could read the host's hardware at all. They now show the host's numbers, with a hover label naming whose machine you're looking at.
- Context size is the real context, not the summed billing counter.
[0.21.1] - 2026-08-17
Fixed
- Opening a channel from the Agents view no longer blanks the app. The channel drawer read a value that was never defined, so selecting a channel threw and took the whole window down with it — the app appeared to simply vanish, with the reason visible only in DevTools.
- Scheduled jobs that deliver to WhatsApp now ask for the recipient up front. WhatsApp can't infer where to post, so a job saved without one failed on its first run with "Delivering to WhatsApp requires target" — hours later, and nowhere near the form that caused it. The editor now asks for the recipient the channel actually needs (a phone number in E.164, a group JID, or a newsletter JID), explains the format, and won't save a job that can't deliver. Channels that can reply where the conversation last happened — Slack, Telegram, Discord — stay optional. If you leave the channel blank and the agent is bound to exactly one, that channel's rules apply.
[0.21.0] - 2026-08-12
Added
- Tool steps you can actually read. Expanding a step used to show the plumbing rather than what happened: a gateway restart read "Updating action, note, reason" above nineteen lines of protocol envelope with its payload encoded twice, and a shell script arrived as a single JSON string full of
\nescapes. Steps now lead with the event. The header names what the call did —restart, or the first line of a script that actually does something, skipping the shebang andset -epreamble. The result is unwrapped and its facts shown as chips (ok yes · pid 504495 · SIGUSR1), with success and failure carried by colour instead of by reading"ok": true; JSON is now the fallback for genuine structure rather than the default. Every step shares one anatomy — Command/Request, then Result — so it reads the same whatever the tool. - The files your agents write are finally reachable. When a turn writes a file, a card now appears under the message — name, type, size — and opens it in a viewer with four levels of zoom: inline card → side panel next to the chat → full content area → its own window (same mechanism as a popped-out chat, so it survives a reload). Markdown renders, code and logs get a monospace view, images/video/audio use the existing players, and anything else offers Save a copy, which writes to your machine through a native save dialog (a browser download in the PWA). The new Files panel — the folder button in the chat header — lists everything on the gateway host instead of only what chat mentioned, newest first, with a dot on files written since you last looked. This matters most on a remote gateway, where "I saved it to ~/report.md" used to be a dead end: the file was on the host's disk with no way to read it. Cards and the viewer work with the plugin you already have; the Files listing needs
joaxclaw-fs≥ 0.12.0 on your gateway (without it the panel offers the one-click install). Read-only by design — the models author these documents, the app shows them. Design notes: docs/files-drawer.md. - JoaxClaw on your phone. The web build is now an installable PWA — serve it from your gateway (or any allow-listed origin), add it to your home screen, and you get the real app on a phone: chat, agents, teams, processes, crons, models, memory, all over the same gateway connection the desktop uses. The phone pairs as its own device (a WebCrypto Ed25519 identity approved once with
openclaw devices approve), so it holds full operator scopes without copying the desktop's token; while approval is pending the connect screen shows the device id and keeps retrying, connecting the moment you approve it. Every surface got a real phone design pass rather than a squeezed desktop layout: a bottom tab bar, master-detail chat with a purpose-built list, a card-based dashboard feed, read-and-run views for Teams and Processes, a stacked Agents overview, full-screen editor sheets, ≥40px touch targets, and Return inserting a newline instead of sending. Background and design notes: docs/mobile-companion.md. - Notifications when the mobile app is in the background. Opt in under Settings → Notifications and a finished agent reply, a fired reminder, or a completed team/process run raises an OS notification; tapping it opens the app on the right chat or view. Browser/PWA only — the desktop app is unchanged — and it covers the app being backgrounded, not fully closed (true push needs gateway-side Web Push).
- Running scripts stay pinned to the top of the chat. A script the agent launches with
script_startnow sits in a sticky strip above the conversation — command, elapsed time, a % bar, Stop, and an expandable output tail — so a long build or test run stays visible however far you scroll, and its result stays put until you dismiss it. It also survives an app reload: the chat re-attaches to whatever the host is still running, where before the script simply vanished from the UI (the reloaded transcript carries no tool calls to recover it from). The reload case needs thejoaxclaw-fsplugin ≥ 0.11.5 on your gateway.
Fixed
- The tables models write now render as tables. Markdown only accepts a table when the
|---|row directly follows the header, but models routinely write one header up top and then rows grouped under section headings — which the strict reading turns into a paragraph of pipes. A 19-row test plan arrived as a single run-on line. Those rows are now re-attached to the header whose columns they match, in chat and in the file viewer alike. Untouched when a document is already well-formed. Wide tables also scroll inside their own frame instead of clipping their last columns in a narrow pane. - A failing panel no longer takes the whole window with it. An error in one panel used to unmount the entire app — a blank window, with the reason visible only in DevTools. The panel now shows what went wrong and the rest of the app keeps running.
[0.20.2] - 2026-07-28
Fixed
- Media from the display-media skill now renders on a remote gateway. Images, video, and audio referenced by a workspace path (e.g.
file://…/color-99.jpg) were resolved through local file access, so when connected to a remote gateway the app looked on the wrong machine and showed a red "could not load" tile. They're now read from the gateway host over the connection, via thejoaxclaw-fsplugin's newhost.readMediamethod (local gateways are unchanged). Requires updating thejoaxclaw-fsplugin on your gateway to ≥ 0.11.4 — via Extensions → update, or the "Install via agent" flow.
[0.20.1] - 2026-07-20
Fixed
- Cron runs no longer clutter the chat list. Finished scheduled (cron) sessions were appearing in the chat sidebar as plain conversations labelled with the agent (e.g. "main"). They're now kept out of the chat list — cron runs belong in the Crons tab, and a currently-running one still surfaces under the Scheduled group.
[0.20.0] - 2026-07-20
Added
- PDFs and documents in chat. You can now attach any file to a message — PDFs, Word/Excel/CSV, PowerPoint, code, archives — not just images and audio. Documents render as tidy file cards (a format-specific icon + filename + size, click to open or download), images and videos get thumbnails, and the file picker, paste, and drag-and-drop all accept every type. Previously a PDF was mis-classified as audio and rendered as a broken player.
- Connection diagnostics (Gateway → Connection). A new Connection log panel shows the live handshake and — front and center — the exact operator scopes the gateway granted, as green/red chips. Filter the raw frames (hide heartbeat noise, or show issues only) and copy the whole thing in one click, so a permission problem is obvious at a glance instead of buried in JSON. An app-wide banner also warns when your token is missing critical scopes and points to the fix.
Fixed
- Remote connections work again after upgrading OpenClaw to 2026.7.x. The upgraded gateway rejects a device-less operator connection from a remote origin, so the app would connect but lose every operator scope ("missing scope: operator.read") — everything failed. JoaxClaw now presents a signed device identity in the handshake (Ed25519, stored securely, paired once and approved on the host with
openclaw devices approve), restoring full operator access over Tailscale/LAN. - Operator role now survives on other networks. A paired device kept its operator role only on the home network; from a café over Tailscale the role silently dropped. The app now caches the gateway's device tokens and resends them — the one auth path that preserves operator scopes on a remote/relayed connection — and re-pairs automatically if a token goes stale.
- The connect handshake negotiates scopes instead of failing. If a token can't hold every scope the app asks for, it now retries with a progressively narrower request rather than being left with none.
> Note: device pairing is a one-time step — the first connection registers the device, then you approve it once on the gateway host (openclaw devices list + openclaw devices approve). Subsequent reconnects just work.
[0.19.0] - 2026-07-13
Added
- Structured questions from agents. Models can now ask you a question as tappable buttons instead of a wall of text — a choice between options, or a simple Yes / No — and your pick is sent straight back. Delivered via an injected
ask-userskill and rendered as an interactive card in the chat. - Host CPU / RAM / GPU on a remote gateway. The dashboard and System Monitor now show the gateway host's resources (not your client machine's) when you're connected remotely — via the
joaxclaw-fsplugin'shost.metrics— with a host badge so it's clear whose numbers you're seeing. GPU is multi-vendor (NVIDIA / AMD / Apple Silicon). - Tracked background scripts. When an agent runs something long — a build, install, deploy, training run, or server — it launches it as a tracked job instead of blocking, and you get a live progress card in the chat: status, elapsed time, streaming output, a % bar when the script prints one, and a Stop button. Jobs run on the gateway host, so progress survives an app reconnect. When a script finishes, the session is automatically woken with the result so the agent can act on it — no polling.
- Running scripts & reminders on the dashboard. The right panel now surfaces the scripts your models are running (with Stop) and pending self-reminders (with a live countdown and cancel).
- "About You" profile. Tell your agents who they're talking to. A new Settings → You panel — and a first-run welcome — lets you set your name and a short description; it's shared as context at the start of new chats, and your name replaces the "JoaxClaw" label the model used to see. A per-chat toggle keeps any single conversation private.
Fixed
- "reply session initialization conflicted" recovers better. Mid-conversation on a remote gateway, a stuck server-side reply context is now aborted before retrying; and reminders / script wake-ups are no longer delivered to ephemeral sub-agent sessions, which was a source of the conflict.
- Saving a model provider no longer errors. Editing a provider's models (e.g. Google) previously tripped the gateway's array-shrink guard; the save now names the exact array paths (including nested ones) so it goes through.
- Deleting a chat removes it for good. It now also deletes the underlying gateway session, so it no longer reappears as a "recent" row — and idle sessions are deletable too.
- Opening a chat gives instant feedback. Tapping a not-yet-loaded chat switches to it immediately with a loading spinner instead of hanging on a slow remote history fetch.
- Background scripts pick up your PATH. Long scripts run via your login shell, so tools like
node(nvm / asdf / Homebrew) resolve correctly instead of a stale/usr/bin/node. - Quieter host logs. The app no longer re-sends (and the gateway no longer re-logs) RPCs an older gateway doesn't implement, such as
plugins.list.
> Note: the remote host metrics and script tracking require the joaxclaw-fs plugin ≥ 0.11.2 on the gateway (openclaw plugins install --force openclaw-joaxclaw-fs + restart).
[0.18.0] - 2026-07-09
Added
- The Memory tab, rebuilt as pluggable connections. What was a hardcoded Obsidian view is now a provider-neutral memory manager: Connect a store (Obsidian, or a new zero-dependency Markdown folder), Manage which agents can reach it and whether it's read-only, and Browse its contents in-app — a backlink graph for Obsidian, a notes list + preview for Markdown. Agents reach each store through a generated OpenClaw skill, so adding a backend is a single registry entry. Existing Obsidian vaults are folded into connections automatically, so nothing is lost.
- Memory on a remote gateway. Enabling a connection against a remote gateway now installs its skill, browses its server-local stores, and renders the Obsidian backlink graph — all on the gateway host, over the connection, via the existing
joaxclaw-fsplugin (no separate install). When the host doesn't have the plugin yet, the tab offers a one-click Install via agent (the same flow as Teams/Processes); local gateways keep working as before. - Env-var credential references for memory stores. A secret field can be an
env:VARreference instead of a literal key, so the plaintext credential stays out of both the generated skill file and localStorage. It's resolved where the request actually runs — the gateway host's environment for a remote store, a name-validated local lookup otherwise. - Live gateway connection-strength indicator. A wifi-style signal meter in the status bar reflects how healthy the gateway connection is — sampling round-trip latency on a rolling window and showing 0–4 colored bars (green / amber / red) with median RTT and loss. Works against a local or remote gateway.
- Sky Light theme. A bright light theme built around whites and soft light blues, with matching gentle sky-toned backgrounds for the app and chat.
- Theme-aware app logo. The logo now matches the active theme — the dark-navy artwork on light themes (e.g. Sky Light), the pale artwork on dark themes — following the OS scheme for
systemthemes. - Recent chats in the sidebar. Reopening the app now surfaces the chats you were last working on, newest first, under the Today / Yesterday / Earlier groups. The list is driven by the gateway's session history (so it survives a restart) and is capped to the most recent few, with Show older to reveal the rest — instead of showing nothing until you re-open a chat by hand.
- Scheduled section in the chat list. Sessions currently being driven by a cron job are grouped under their own Scheduled heading and labelled by the job's name, instead of showing up as a bare agent run.
Changed
- Management surfaces consolidated under Settings. The top navigation is now just the things you use day-to-day; Sessions, Models, and Extensions move into Settings sub-tabs (Connection · Gateway · Sessions · Devices · Channels · Local LLM · Models · Extensions). Deep links (e.g. "manage plugin") jump straight to the right sub-tab.
- Sessions view de-technified. It's now a management view (Chats is where you resume live sessions): name-first rows with the raw session key on hover, plain-language status (Completed / Stopped / Timed out), and a cost-first usage cell with the token breakdown on hover — no more raw keys or "ctx tokens" jargon.
- Extensions redesigned into a scannable library. A segmented Skills / Plugins control with search and a context-aware Add, rows grouped by attention (Needs setup → Active → Off), each leading with name + description + clear state, with Configure/Update inline and the technical details tucked into the expanded row.
Fixed
- Transient "reply session initialization conflicted" turns now recover on their own. When the gateway briefly races two initializations of a fresh session's reply context, the turn is retried automatically — up to three times with an escalating backoff, across both the agent-stream and chat-state error paths — instead of stranding a dead ⚠ bubble that you had to re-send by hand.
- Memory errors say what actually went wrong. The real cause (ECONNREFUSED / TLS certificate / timeout) is surfaced instead of a bare "fetch failed", and the tab shows just the message rather than a raw
{"code":…}blob. - Obsidian over local HTTPS is reliable. A local Obsidian host's self-signed certificate is tolerated (and TLS skipped via
node:httpsfor localhost), so a local vault connects without cert errors. - Memory connection tests run on the right machine. For a remote gateway, a connection is tested from the gateway host rather than your local machine, so the result reflects what the agents will actually see.
[0.17.1] - 2026-07-03
Added
- Emoji shortcodes in the chat composer. Type
:followed by a name (e.g.:tada) to get an autocomplete popup — navigate with ↑/↓, insert with Enter/Tab/click, dismiss with Esc — or type a full:tada:(closing colon) to auto-convert it in place, just like Slack/Discord/GitHub. Backed by a curated, in-bundle shortcode set (no dependency); the:won't trigger inside things likehttp://or12:30. - Remote gateway management in the Gateway tab. The config editor now reads and writes the gateway host's config over the connection (via the gateway's own config RPC, which hot-reloads it) instead of silently editing the local machine's file, and Restart / Safe / Stop — which have no remote API — open a chat asking an agent on the gateway host to run the command (you approve it). Gateway status is derived from the live connection. Local gateways keep the existing file/CLI behaviour.
Fixed
- Plugin versions on a remote gateway now reflect the host, not your machine. The Plugins tab merged the local
openclaw plugins listover the gateway's, so a plugin you also had installed locally (e.g. a dev copy ofjoaxclaw-fs) masked the remote version — it never changed on refresh no matter how the remote plugin was upgraded. The local CLI is now skipped entirely for a remote gateway, so metadata comes solely from the gateway.
[0.17.0] - 2026-07-03
Added
- Emoji reactions on chat messages. Hover any message (yours or the assistant's) and a + reveals a compact emoji picker; your reactions render as pills under the message and toggle off with a click. Reactions persist locally in an append-only log (
~/.openclaw/reactions/reactions.jsonl) and survive restarts. (Personal annotations for now — the gateway emits no chat reaction events, so nothing is sent upstream.) - Model reactions read as a chip. When a model reacts to a message via the channel
messagetool (action: "react"), it renders as a friendly "Reacted 👍" chip instead of a raw tool card — with a "Reacting…" state while in flight and a clear "Couldn't react — …" state (e.g. Action react requires a target.) on failure. This reflects the model's channel activity (the id is a channel message id), not an in-chat reaction. - Gateway update detection. JoaxClaw now detects when the connected OpenClaw gateway has a newer version available and offers to update it — the same way it already does for the app and the
joaxclaw-fsplugin. A slim app-wide banner ("OpenClaw X is available · the gateway is on Y") and the Update button in the Gateway view (now labelled **Update → X** when an update exists) run the existing agent-drivenopenclaw update+ restart. Detection uses the gateway's own channel-awareupdate.status, so it respects your update channel (stable / beta / git checkout) rather than guessing.
[0.16.0] - 2026-07-01
Added
- Provider logos across the model UI. Each provider now shows its brand mark (monochrome, so it adapts to any theme) next to the model — in the chat top-bar model picker and throughout the Models tab (providers list, provider header, usage table). ~40 providers covered, with a clean fallback for the rest; compound provider ids like
ollama-cronresolve to the right logo. - Redesigned Local Models screen (Models → Local models). A clear two-column layout: Available to install on the left (compact rows with publisher avatar, capability + input-modality chips — text / image / audio / video — and an Install arrow) and Installed on the right with fixed-width, aligned Memory / Provider status pills. The engine selector is now a spacious, logo-led segmented control showing each engine (
ollama,ollama-cron, …) with its URL. - RAM footprint bar for local models. Each installed model shows a stacked bar of its memory footprint — model weights plus a context/KV-cache buffer — against the machine's RAM, or VRAM when the model is loaded on the GPU. While a model is loaded it uses the real resident size and context from Ollama, so a model loaded at a large context window reads its true footprint instead of an estimate; turns red if it would exceed the pool.
- Richer plugin configuration forms. The plugin Configure dialog now renders typed, grouped fields (API key, LLM settings, and behaviour settings) instead of only an API key — schema-driven when the gateway provides a plugin's config schema, curated otherwise, with the raw JSON editor still available.
- Reminders — let an agent ping itself later. An idle or waiting agent can schedule a self-ping (a message it writes to its future self) and cancel it when done, via the
joaxclaw-fsplugin (requires plugin ≥ 0.5.0). Sessions waiting on a reminder show an alarm-clock badge with a live countdown in the Sessions view, and you can cancel it with one click. - Plugin update detection + one-click upgrade. JoaxClaw now compares the installed
joaxclaw-fsversion against the latest published on npm and offers to upgrade — a slim app-wide banner (like the app-update banner) and an Update button on the plugin's row in Extensions. The upgrade runs the force-reinstall + gateway restart via an agent. - Unified chat list. The chat sidebar is now a single, triage-friendly column, and Team/Process sub-agent threads are labelled by their task instead of a generic agent id.
Fixed
- Fixed the Azure provider logo, which rendered faded/incorrect in a single color.
- Sub-agent yields (threads) survive reconnect and reload — a session yielded to a still-running sub-agent is correctly shown as live, and pending/running yields are reconstructed on reconnect and placed on the in-flight turn.
[0.15.1] - 2026-07-01
Fixed
- The title bar's bottom border is continuous again. Electron 43 (0.15.0) activated the native window-controls overlay on Linux/Wayland; its opaque region sat over the custom title bar, duplicating the app's own window buttons and cutting the bottom border under them. Every window already draws its own themed title bar, so the native overlay was removed — the border now runs edge to edge.
- A live chat stays marked as running in the left list. Opening a running session moved it from the green "Live Sessions" zone into a dated group and dropped its running cue. The conversation row now shows a green pulsing dot while its session is still running (or a message is streaming).
[0.15.0] - 2026-07-01
Added
- Scalable theme management with import/export. A dedicated Themes screen with a gallery (apply / duplicate / delete / import / export), a full editor (colors, base mode, radius, font, icon family), a Backgrounds editor, and a live preview. Themes are a portable
.joaxthemepackage (a zip): a versionedtheme.jsonplus optional per-surface background images, so they're easy to share and reuse. The built-in themes now live as readable files in the repo and load as the single source of truth. - Theme background images. Optional wallpapers behind the app and the chat, each with opacity, blur, and fit, packaged with the theme on export (recommended size shown in the picker). Image bytes live on disk, never in localStorage.
- Curated art for every base theme (royalty-free, no attribution): Midnight (indigo Milky Way / starfield), Ocean Dark (navy waves / dark water), Forest (dark pines / misty green), and Rose Light (pale peonies / white blossoms, tuned for the light theme).
- Retro Terminal theme. A new green-phosphor CRT terminal theme for night coders — green-on-near-black with amber highlights, a monospace font stack, and near-square corners.
Changed
- Theme editing moved out of Settings into the new Themes screen; Settings keeps status-bar, zoom, and update options.
Notes
- Base themes always load from the bundled repo files (only user-created themes persist), so updated presets and their backgrounds take effect on upgrade.
[0.14.1] - 2026-06-30
Changed
- Smarter "model stopped responding" detection. The stall indicator was a single flat timeout that false-alarmed while a slow local model loaded, during a model's pre-first-token reasoning, and when the connection (not the model) dropped. It's now phase-aware and heartbeat-aware: a generous first-token budget (model load / context encoding) vs a tighter inter-token gap once output is flowing; the gateway heartbeat distinguishes a dropped connection ("reconnecting…") from a genuine model stall; and running tools, sub-agent runs, and Ollama prompt-token ingestion no longer count as a stall. Richer states — "waiting for the model…", a genuine stall, or "reconnecting…" — replace the single binary alarm.
Fixed
- The red "model stopped" notice no longer contradicts the "waiting for the model" counter. The chat message body had a second, independent stall detector on a flat 15-second timer, out of sync with the composer's indicator. Both surfaces now share one phase-aware detector, so they can't disagree.
[0.14.0] - 2026-06-30
Added
- One-click gateway self-update. An Update button in the Gateway view asks the connected agent to run
openclaw updateon the gateway host, then restarts the gateway (deferred, so the agent's turn finishes before the connection briefly drops). Because the agent always runs on the gateway host, this works for local and remote gateways alike. - Configurable Obsidian access for agents. A new Agent access control in the Memory view — Off / Read-only / Read & write — governs the
obsidian-memoryskill the app writes for gateway agents. Off removes the skill (agents can't reach the vault), Read-only exposes list/read/search only (no write endpoints), and Read & write keeps full access. Defaults to read-write to preserve prior behavior. (Local-host scoped: the skill points agents at the vault's Local REST API onlocalhost.) - Team member titles on the flow graph. Each member's title (e.g. QA Manager, Senior Developer) now shows on its node in the Team graph, with the underlying agent as a subtitle — so you can see what each member is meant to do at a glance. Titles stay correct even when the same agent is reused for several members.
Changed
- Native window shadow + rounded corners. Upgraded Electron 33 → 43 and switched the app's frameless windows (main, pop-out chat, About) from transparent to opaque, so the compositor draws a native drop shadow and rounded corners that match the rest of the desktop — JoaxClaw windows previously had neither on Linux. Native on macOS / Windows 11 / Linux-Wayland; Linux X11 falls back to square corners without a shadow. Removes the old CSS corner-clipping workaround.
Notes
- The Electron 43 upgrade brings Chromium 150 / Node 24; dependency advisories were resolved (
npm auditreports 0 vulnerabilities).
[0.13.0] - 2026-06-30
Added
- Pop-out chat windows. Move a chat into its own window — and bring it back — so you can keep several conversations open side by side. Each pop-out connects to the same gateway and streams its session live and independently; it hides the sidebar and locks to that one chat, with a Return control (closing the window also returns the chat). The main window hides a chat while it's popped out and restores it on return ("move", not mirror). Along the way: the app windows now have rounded corners, and the chat header's Basic/Advanced switch and Reasoning / Actions / Context toggles are collapsed into a single compact multi-select menu so the header (especially in the narrow pop-out) stays uncluttered.
- System-tray activity + About window. The tray menu and tooltip now show how many agents and teams are running (click either to jump to that tab); the tray icon uses the app logo. A new About JoaxClaw tray item opens an About window with the app version, a check-for-updates button, links to the GitHub repository and sponsorship/donations, and copyright.
Fixed
- The delete button on Processes and Team blueprints is reachable again. The trash button was rendered with an inline
opacity:0that overrode its hover-reveal class, leaving it permanently invisible — so there was no way to delete a process or a team. The hover reveal now works (hover a row → trash → confirm). - Clean agent names instead of raw session keys. A Team/Process sub-agent's opaque session key (
agent:<id>:subagent:<uuid>) leaked verbatim into the chat title bar and as asubagent · <uuid>entry in the chat list, because the key parser only understood the legacy<id>@<uuid>form. Names now resolve consistently across the chat header, chat list, and Sessions view — your rename → the Team-derived name → the gateway's display name → the agent id parsed from the key — and existing conversations are fixed too, not just new ones.
Notes
- CI: GitHub releases now self-trim to the 5 most recent on each publish (older releases are removed; their git tags are kept), with an on-demand "Prune old releases" action.
[0.12.5] - 2026-06-29
Added
- Readable names for Team-spawned sub-agents. A Team/Process sub-agent is only known to the gateway by an opaque key (
agent:<id>:subagent:<uuid>), which surfaced verbatim in the chat list, the Sessions view, and the dashboard. They now show a friendly name derived from the team and the controller's task label for that worker (e.g. "Research Team · Web Searcher"), persisted across reloads. Resolution order is your explicit rename → the team-derived name → the gateway's own display name → the key.
Changed
- Team graph now lays out loops and branches instead of a flat line. The graph editor used a longest-path pass that collapsed any team with a feedback loop (or no entry node) into a single column. It's now a layered (Sugiyama-style) layout: cycles are detected and their back-edges drawn as arcs, columns are assigned left→right over the resulting DAG, and node order within each column is chosen to minimise edge crossings. A freshly-compiled team auto-arranges on open, while a manual arrangement is preserved.
Fixed
- Agents graph no longer hangs or renders as a tall, tiny sliver. The hierarchy layout runs during render; its longest-path walk increased a node's level on every longer path, so a cyclic
allowedSubAgentsrelationship (A→B→A, or an agent that lists itself) looped forever and froze the tab — and a stop-gap cap then flung cycle nodes far down the canvas, shrinking everything. It now places each agent at its shortest distance from a root: compact, and cycle-safe by construction.
[0.12.4] - 2026-06-28
Changed
- Clearer bottom-nav labels — "Updates" is now where you'd expect. The bottom of the left nav had a gear labeled Settings that actually opened the gateway connection view, while the app's real settings (themes, icons, status bar, app prefs, and Updates) hid behind a palette icon labeled Theme. They're now named for what they do: a Gateway item (server icon) for the connection/devices/channels, and a Settings item (gear icon) for the app settings — so the auto-update controls live under the conventional Settings gear.
Fixed
- Release installers now publish correctly. GitHub now makes a published release immutable, so the release workflow — which published the release before attaching the
.deb/.dmg— could no longer upload them, leaving the release with only GitHub's auto-generated Source code archives. The workflow now creates the release as a draft, attaches the installers to the draft, then publishes it. (0.12.3's installers never attached for this reason; 0.12.4 supersedes 0.12.3 and includes all of its changes.)
[0.12.3] - 2026-06-28
Added
- Live action trail of sub-agent work in the Team/Process monitor. The monitor previously showed a member's streamed text but not what it actually did — every tool call except
sessions_spawnwas dropped. The activity log now records each tool call the controller and its workers run, attributed to the emitting agent with a short argument summary (e.g.🔧 research-worker: read_file (src/lib/foo.ts)), and surfaces failures (⚠ research-worker: bash failed). The trail is capped so it stays bounded on tool-heavy runs. - Claude-style colored diffs for file edits in chat. When the model modifies a file, the change renders as a rich diff instead of raw text/JSON — color-coded additions/deletions, old/new line numbers, a +N/−M summary, a unified⇄split toggle, collapse, and syntax highlighting. It's detected from every shape an edit arrives in: model-written
`diffblocks, gateway<edit>/<write>tags, edit tool-call args (old_string/new_string,apply_patch), and edit tool results that carry a unified patch.
Changed
- Native window controls (min/max/close) follow the app theme. The OS-drawn title-bar buttons baked in their colors at window creation; they now re-tint to match the active theme. (Applies on Windows/macOS, where the OS draws the overlay; Linux uses the app's own title bar.)
Fixed
- Your theme selection persists across reloads.
main.tsxre-applied the default (dark) theme after the settings store had already restored your saved theme, so every reload reset it to dark. It now applies the saved theme, falling back to the default only on a fresh install. - Team/Process run progress survives a reload and a reconnect. Runs were persisted only at launch (
stepsDone: 0) and on completion, so reconnecting to a gateway mid-run — or reloading the app — showed the monitor as if the run had just started. Progress is now persisted on each advancing event (steps,[PROGRESS]markers, delegations) and mirrored to localStorage, so the monitor repaints the real progress instantly on reload while the gateway reconciles the rest on reconnect (a run that finished while the app was closed self-heals to done). - The Theme view no longer traps you while disconnected. On the connect screen every section but Theme was disabled in the nav rail, so opening Theme left no way back to the connect screen. Dashboard stays clickable while disconnected and routes back.
- Dashboard no longer treats team runs as processes. Team runs share the processes store, so the dashboard double-listed running teams under "Active", counted them in the "processes running" chip, and mis-navigated to the Processes tab. Team runs are now split out by blueprint id — they appear only in the Teams section (→ Teams tab), with their own "teams running" chip. Drawer times also roll up through hours/days (2h 5m, 1d 3h, 2d ago) instead of large minute counts.
- Monitor step marker no longer sticks on a handoff after a delegation. The Team Progress marker used
i === stepsDone, but the step list interleaves handoff/review transition nodes thatstepsDonedoesn't count — so a finished handoff held the marker while the next agent was already working. Transition nodes now read as done immediately and the marker follows to the next agent; the progress bar also reaches 100% instead of stalling at, e.g., 3/5. - Chat no longer looks stopped when an agent delegates. This gateway ends the run with an empty
finalat thesessions_yield(the pause to wait for a sub-agent), then auto-resumes and streams the real answer in a laterfinal. The client treated that first emptyfinalas "done" and unsubscribed, so the resumed answer never arrived until you sent another message. An emptyfinalwhile a sub-agent thread is still in flight is now treated as a yield boundary — the stream stays attached and the resumed answer comes through. - Reliable chat auto-scroll. The auto-follow
ResizeObserverattached on mount, but the empty-conversation state didn't render the scroll container, so it watched nothing and never re-attached when messages arrived — silently killing auto-scroll for that conversation. The container is now always mounted, and sending a message snaps to the bottom. - Reused agentId no longer breaks conditional routing in teams. A branching team that used the same agentId for multiple members (e.g.
coder-workeras both a Code and QA agent) compiled a corrupt graph — a route landed on every member sharing that id and feedback edges resolved to the first match, orphaning steps. Routes/branches now resolve to a unique member by role (with agentId kept as a backward-compatible fallback), so reused-agentId members are distinguishable. Existing unique-agentId routes are unchanged.
[0.12.2] - 2026-06-27
Added
- Device pairing management (Settings → Devices). Full lifecycle for the gateway's paired clients: approve/reject incoming pairing requests, view paired devices (expandable, with copyable
deviceId/publicKeyand a per-token table), and remove devices or rotate/revoke their tokens. The panel is read-only withoutoperator.admin; destructive actions use inline confirms and a last-admin-device guard blocks locking yourself out of device management; the list live-updates via the gateway'sdevice.pair/device.pairing.events. Newstore/devices.ts,components/gateway/DevicesPanel.tsx. - Inline sub-agent threads in advanced chat. When the assistant spawns a sub-agent (
sessions_spawn), it now appears as a Slack-style collapsible thread anchored at the spawn point — a chip showing the agent, a live status, and a one-line peek of what it's doing; expand it to watch the sub-agent's own reasoning, tool calls, and answer stream live. The sub-agent's frames are linked back to the run via each frame'sspawnedBy, replacing the cold "waiting for session" hourglass that previously hid all of its work.
Changed
- Clearer "thinking" vs. answer in chat. Reasoning no longer dumps fully expanded above the reply: it streams open only while the model is still thinking, then auto-collapses the instant the answer starts into a quiet "Thought for Ns" pill (tap to reopen). Reasoning extraction now also recognizes
<thinking>/<reasoning>/<thought>(not just<think>), so more models that stream their reasoning as content get the tidy treatment.
Fixed
- Team/Process runs no longer falsely complete while a member is still working. Completion was force-marked "done" by a 10-minute hard cap even when the session tree was still active, so any member that legitimately worked past it ended the whole flow early. The completion watcher now finishes a run only on a confirmed sustained-idle streak (never on the timer); the wall-clock is just a generous watcher lifetime (10 min → 2 h) that pauses without completing if reached while still active (re-arming on the controller's next
final); and it countshasActiveSubagentRun, so a yielding controller with live members still reads as busy.
Notes
- Added a Shared Workspace field to the Teams builder as groundwork toward teams that edit a shared repository — members are spawned with a shared
cwd, handoffs are filesystem-first, and steps are git-checkpointed. The app-side wiring is in place, but a member reliably editing the host repo still depends on a gateway-side spawn-runtime change and is not complete yet.
[0.12.1] - 2026-06-26
Added
- Reusable teams — run a saved team against a per-run task. A team blueprint is now a reusable design: a new "Task for this run" box supplies a concrete objective at launch, and member tasks / the output contract may use an
{objective}placeholder that's substituted in (threaded throughbuildLaunchPrompt, recorded on the run, and shown in the monitor). - Agent-launchable team runs. Agents can start a saved team via the
joaxclaw-fsplugin's newteams.runmethod ({ id, task, autorun? }), which records a one-shot<id>.runrequest.json; the app polls for it, pre-fills the task, optionally auto-launches, and clears it (the app still owns prompt-building and the run monitor). Documented in theteams-blueprintskill (v2). Requiresjoaxclaw-fs≥ 0.4.0 on the gateway host.
Fixed
- Team runs no longer report "complete" after the first step. This gateway emits no
waiting/delegatingchat state, so worker sub-sessions weren't linked to the run and a controllerfinal(fired on everysessions_yield) ended it early. Runs now link workers via each frame'sspawnedByand complete only via the gateway idle-poll (includeRoot=true), so neither a controller nor a workerfinalends the run prematurely; a worker error/abort no longer fails the whole run (controller-only).
Plugin
openclaw-joaxclaw-fs0.4.0 adds theteams.rungateway method (agent-launchable team runs). Published to npm via CI on the version bump.
[0.12.0] - 2026-06-26
Added
- Built-in app auto-updater. JoaxClaw now checks its own GitHub Releases for a newer version — shortly after launch, every 6 hours, and on demand from Settings → Updates — and surfaces an app-wide banner when one is found, with the release notes one click away. From the banner you Download the right installer for your OS (with live progress) and then Install, with the handoff matched to the platform: on Windows the NSIS installer runs and the app restarts itself; on macOS the
.dmgis mounted so you drop the new app into Applications; on Linux the.debis installed via a graphicalpkexecprompt (falling back to revealing the file). You can Skip a version or dismiss the banner, and turn the automatic check off entirely. Because the builds are unsigned, the flow is "download + assisted install" rather than fully silent on macOS/Linux — no code-signing required. Newelectron/main/updater.ts,store/updater.ts, andcomponents/layout/UpdateBanner.tsx; semver-aware, so it never offers a downgrade and treats a finished release as newer than its own prerelease. - Team/Process runs survive an app restart. A run executes on the gateway (independent of the app), so restarting the app no longer loses it: the in-flight run is persisted with its controller session key, and on reconnect the app re-attaches — re-routing the run's live events and reconciling completion against the gateway's live session tree (
sessions.list). A run that finished while the app was closed is marked done; one still going keeps tracking. Previously a restart marked the run "Interrupted." - Zoom the whole app with Ctrl/⌘ + and Ctrl/⌘ − (and Ctrl/⌘ 0 to reset). Uses Electron's native frame zoom, so it scales everything — including inline-pixel styles a CSS font-size couldn't reach — and the level persists across restarts. Also exposed as a −/Reset/+ control under Settings → App for discoverability.
- Chat "Basic" mode — a friendlier presentation for run-of-the-mill users, toggled from a Basic | Advanced switch in the chat header (Advanced stays the default). Instead of tool-call cards, reasoning dumps, and a bare blinking cursor, Basic mode shows a calm plain-language activity trail while the assistant works — completed steps as a checklist (✓ Searched the web, ✓ Read files) with the current action live at the bottom (Running a command…, Working with a specialist…, Thinking…). The answer renders normally, with an opt-in Details disclosure to expand the full advanced view for any one message. While the model is working with no specific tool, the status rotates through ~85 playful "thinking" verbs (Frolicking…, Pontificating…, Reticulating…, Noodling…) in the spirit of Claude Code's spinner words. It's a presentation layer over the existing event stream — no gateway changes.
- Extensions → Plugins now lists the gateway's full plugin registry, not just plugins that already have a config entry. Previously a plugin that was enabled by default but unconfigured (e.g.
browser,canvas,openai) was invisible; now every installed plugin shows with its enabled/disabled state and the all/enabled/disabled filter. Toggling or configuring a registry plugin adopts it into config; untouched ones are never written back (so the config isn't polluted with all ~90 stock plugins). The registry is read from the local CLI for a local gateway and from the gateway's ownplugins.listwhen remote.
Fixed
- Chat reliably stays pinned to the bottom while content grows. Auto-follow keyed off a hand-rolled signature of the last message's fields (content/reasoning/tool-call lengths + streaming flag), which missed growth it didn't enumerate — Basic-mode action steps, images finishing load, markdown reflow. It now watches the content box with a
ResizeObserverand jumps to the bottom on any height change while the view is pinned, so streaming never drifts above the fold. - Teams/Processes runs no longer report "completed" while workers are still running. The run tracker registered a spawned worker only if the
sessions_spawnresult carriedkey/sessionKey, but the gateway returns it aschildSessionKey— so no workers were tracked,_pendingSubSessionsstayed empty, and the Team Lead'sfinalended the run early (often within seconds,stepsDone: 0). It now readschildSessionKeyand registers the awaited sub-session onwaiting/delegating. As a robust safety net (event inference can still miss workers), when the controller finishes with no tracked workers the app now asks the gateway which descendant sessions are still alive (sessions.list→parentSessionKey+status/hasActiveRun) and completes the run only once the whole session tree is idle, bounded by a hard cap so a missed signal can't hang it. - A chat no longer hangs forever when a tool restarts the gateway. Running something like
systemctl --user restart openclaw-gatewaykills the WebSocket mid-turn, so nofinalevent ever arrived and the message spun indefinitely with a live Stop button. The app now detects the connection drop, finalizes the in-flight message, and shows a live notice on it — "The gateway connection dropped (it may be restarting) — reconnecting…" — that flips on its own to "Gateway restarted and is back online — send a message to continue." the moment the auto-reconnect succeeds. - Saving plugin/skill changes no longer fails with "Unrecognized keys" config errors. The Extensions save wrote display/registry fields (
name,description,source) intoplugins.entries./skills.entries., which the gateway's strict config schema rejects; it now strips them and only persists genuine config keys. Untouched discovered skills (and registry plugins) are no longer written at all, so a single toggle doesn't rewrite the whole catalog. - Setting a model provider's API key no longer fails with a Base URL validation error. In the plugin Configure form, leaving the optional Base URL field empty used to wipe the provider's endpoint; the gateway then merged the plugin's default empty
baseUrland rejected the whole patch (models.providers.<id>.baseUrl: Too small: expected string to have >=1 characters), so saving just an API key failed. The form now only writes Base URL when you actually type one and never clears an existing endpoint (use the Advanced tab to remove an override). - The "model stopped — send a message to continue" banner no longer appears while the model is still ingesting the prompt. The staleness detector only watched content/reasoning/tool activity, so a long prompt ingest (which emits no output yet) tripped it even at, e.g., 84% / 481 tok/s; it now treats live prompt-token progress as activity and suppresses the banner while ingestion is advancing.
- Slow local models no longer make the chat go silent.
chat.sendwas awaited with the default 30 s request timeout, so a local model that took longer than 30 s to produce its first token tripped the timeout, the message was marked not-streaming, and every indicator vanished (no "thinking…", no prompt bar, no Stop button) while the model kept generating. The turn's lifecycle is driven by the event stream, not bychat.send's reply, sochat.sendis now sent without a timeout (and not awaited) — the indicators and Stop button stay until the stream actually ends. The composer also re-enables immediately instead of locking for the whole turn. - The chat "Processing prompt…" bar no longer disappears when Ollama goes a few seconds without printing a progress log line. It previously expired after a 4 s gap; it now persists through quiet gaps (the spinner keeps turning) and is cleared on the actual lifecycle — when prompt eval finishes or output starts — via a new
reset()instead of a short idle timer. - Model-pull progress no longer jumps backward or blanks out. Ollama announces layers incrementally (so the raw overall % drops when a new layer is discovered, and disappears during the manifest/verify phases); the download bar is now clamped to move only forward and keeps its last value through those phases, with a live downloaded-bytes readout (e.g.
3.4 / 8.2 GB) so there's always feedback.
[0.11.0] - 2026-06-25
Added
- Local model management (Models → Local models). A new tab to manage models on a local Ollama engine: see installed models (size · params · quant · loaded), pull a new model by name with live download progress, delete, and "Add to provider" to register a model into the gateway config so agents/the picker can use it. Works on a **local and remote gateway — it's routed through the
joaxclaw-fsplugin's newengines.pull/engines.pullStatus/engines.deletemethods (the plugin runs on the engine's host). Requiresjoaxclaw-fs≥ 0.3.0. A Discover browser presents a curated, searchable catalog of popular models (publisher, capability badges — tools / vision / reasoning / code / embedding — and a size-per-variant picker) so you can pull without knowing exact names; anything else is still pullable via the free-text field. Each installed model expands to show details (family, parameters, quantization, context length, license) and can be loaded/unloaded from memory; downloads show an overall** progress %. Newlib/modelManager.ts,lib/modelCatalog.ts,store/modelManager.ts,components/models/LocalModelsPanel.tsx. (LM Studio's API can't download models, so it's a later, list-only phase.) joaxclaw-fs0.3.1 addsengines.pull(streamed Ollama pull →pullId, polled viaengines.pullStatus, with overall %),engines.delete,engines.show(model details), andengines.keepAlive(load/unload).- Talk mode shows the agent's activity. During a voice call, a collapsible Agent activity panel lists the actions the agent takes — each tool call with a live status (running / done / failed), inline progress, and expandable args + result — so it's transparent what's happening while you talk (e.g. "🔍 searching the web…"). Driven by the
tool.call/tool.progress/tool.resultTalk events into a per-call timeline instore/talk.ts.
[0.10.0] - 2026-06-23
Added
- Talk mode (Phase 1) — voice conversation with your agent. A new Talk tab: click to start, speak hands-free, and hear the agent reply, with a reactive orb (no avatar yet), live two-sided captions, mute, tap-to-interrupt (barge-in), and a tool-activity indicator. Built on the gateway's realtime Talk API — it owns VAD, barge-in, turn-taking and the agent "brain", so JoaxClaw is the client: mic → PCM16 (AudioWorklet, echo-cancelled) →
talk.session.appendAudio, andtalk.event(speechStart/transcript./audio/tool.) drives an interaction state machine. Uses therealtimemode over thegateway-relaytransport (works local and remote), and requires a configured realtime voice provider (e.g. Google Live Voice) — its key goes attalk.providers.<id>.apiKeyon the gateway. ElevenLabs/Deepgram are transcription-only and can't drive realtime Talk; thestt-ttspath needs amanaged-roomtransport that's a later phase. The settings bar lists exactly which providers fit the mode and whether each has a key, and lets you set the realtime provider's key inline (written totalk.providers.<id>.apiKey). Four switchable visualizers (Orb, Bars equalizer, Radial spectrum, Blob) react to the live audio — FFT-driven for the bar styles via the audio engine's analyser, source-switched between your mic and the agent by state, persisted across sessions, and WebGL-free. A "Talking to …" header shows who actually answers — withagent-consult(the default) the realtime voice is a front-end and the reply comes from your agent's model; you can pick which agent answers and switch the brain (agent-consult/direct-tools/none). Newstore/talk.ts,lib/talkAudio.ts,components/talk/TalkView.tsx; design + roadmap in src/lib/TALK.md. Avatar, expressions, stt-tts, and a WebRTC transport are later phases.
Fixed
- Native
<select>dropdowns no longer flash white on open —applyTheme()now sets the documentcolor-schemeto match the active theme (with a dark default for first paint), so Chromium renders native control popups in the right scheme app-wide.
[0.9.0] - 2026-06-22
Added
- Open-sourced under MIT. Added a
LICENSE(MIT), a public ROADMAP.md,CONTRIBUTING.md, aSECURITY.md, and GitHub issue/PR templates; refreshed the README. Internaldocs/architecture notes were removed (code-adjacentsrc/lib/*.mdnotes stay). - Schedule a team to run on a CRON job. The CRON editor's Payload tab has a new "Run a team" kind — pick a configured team and the job fires its Team Lead launch prompt to the team's controller agent on schedule. Because the gateway runs the schedule and the agents, the team executes unattended — no app needed. Reuses the existing
buildLaunchPrompt(same path as the manual Run button); the job is a normalagentTurnunder the hood, with the team recognised again on edit so it pre-selects. (Note: the polished per-node run timeline only records while the app is watching, but the team runs fully either way and the controller/sub-agent sessions are logged on the gateway.) - Configure plugins from the app (incl. API keys). The Extensions view previously only enabled/disabled plugins; each plugin now has a Configure action that opens a settings dialog. It surfaces the plugin's API key routed to the correct place in the gateway config — model providers →
models.providers.<id>.apiKey(with an optional base-URL override), TTS providers →messages.tts.providers.<id>.apiKey, web-search plugins →tools.web.search.apiKey— plus an Advanced tab for the rawplugins.entries.<id>config (behaviour/llmgating). Keys can be literals or env-varSecretRefs (preserved read-only). All writes go throughconfig.patch, so it works on local and remote gateways. Each plugin also shows a status badge — a green Configured check when its API key is set (literal or envSecretRef), or an amber Needs key when one is required but missing. Newlib/pluginConfig.ts(curated key routing + completeness) +PluginConfigModal; curated paths validated against the gateway config schema, with unit tests.
Fixed
- Hardened the "Install via agent" script. It used
openclaw plugins allow— not a real subcommand, so it was a silent no-op (enabling relied entirely onenabledByDefault). The script now usesopenclaw plugins enable, runsinstall --forceso re-runs upgrade instead of failing, and adds anopenclaw plugins inspectguard that aborts before the gateway restart if the plugin didn't register (so a failed install surfaces instead of a misleading "done"). Step output is kept visible for diagnosis. Same corrections applied to the plugin README and Help → Remote Teams. Verified end-to-end; covered by a regression test.
Changed
- Release CI no longer uploads the
.deb/.dmgas 90-day Actions artifacts — they're attached to the GitHub Release (separate storage), so the duplicate copies just consumed the account's Actions-storage quota.
[0.8.0] - 2026-06-21
Added
- Per-channel policy editor. Each configured channel's Edit dialog now has a Policy tab for the access + capability controls the gateway exposes under
channels.<id>: DM policy (pairing/allowlist/open/disabled), a DM/group allowlist of sender IDs, allow-bots, group policy, and an action-permissions grid (what the agent may do on the platform — react, post, pin, moderate, roles, …). Policy can be set for the whole channel or overridden per account or per group (channels.<id>.accounts.<id>/.groups.<id>). Curated per provider (Telegram, Discord, Slack, Matrix, WhatsApp) from the gateway config schema; other channels keep using the Advanced raw editor. Newlib/channelPolicy.ts+ChannelPolicyEditor; writes go throughconfig.patch(withreplacePathsfor allowlist edits). Verified end-to-end against a live gateway. - Per-engine model lists in Settings → Local LLM. Each reachable local engine now shows how many models it's serving, expandable to the full list. Works locally (via a new
ollama:fetchmain-process IPC, which isn't CORS-bound) and on a remote gateway (via the plugin'sengines.fetch). Parses both Ollama (/api/tags) and OpenAI-compatible (/models) responses. NewfetchEngineModels()/parseModelIds()inlib/localEngines.ts, with unit tests. - The model picker offers each engine's installed models. Beyond the models declared in the gateway config, the picker now also lists models a local engine is actually serving (e.g. an Ollama model you pulled but never added to config), tagged
installedand still selectable since the provider routes by name. Gateway-aware viafetchEngineModels, so it works on a remote gateway too. Used across the Agents, Agent editor, and CRON editor pickers. - Isolated cron-instance detection works for every engine, not just Ollama. The "second instance on a companion port" convention (Ollama
:11434→:11435) is now generalized to<default port>+1for all known engines (LM Studio, vLLM, llama.cpp/LocalAI, Jan, KoboldCpp), so the Local LLM Engines panel can flag an isolated instance and prompt for an<engine>-cronprovider regardless of engine. Unused ports just probe down — no false positives.
Fixed
- Deleting a channel (or removing an account) whose config contains arrays — now including policy allowlists — no longer fails the gateway's array-shrink guard: the store collects the block's array paths and passes them in
replacePaths.
Removed
- Dead Ollama-only helpers
checkOllama/resolveOllamaUrlinlib/ollamaHealth.ts(which now only exports the widely-usedgatewayHost/isLocalGateway).
[0.7.1] - 2026-06-20
Added
joaxclaw-fsplugin is published to npm asopenclaw-joaxclaw-fs, so installing it on a gateway host is one command:openclaw plugins install openclaw-joaxclaw-fs && openclaw plugins allow joaxclaw-fs. A new CI workflow (.github/workflows/publish-plugin.yml) publishes the package automatically whenever itspackage.jsonversion changes onmain, version-guarded so unrelated pushes are a no-op (requires anNPM_TOKENrepo secret). The in-app Help → Remote Teams steps and the plugin README now lead with the npm install.
Changed
- "Install via agent" now installs the plugin from npm instead of embedding its files as base64 in the chat message. The script handed to the host agent is now a short
openclaw plugins install openclaw-joaxclaw-fs+ allow + restart. This removed the renderer's dependency on theteams:installScriptmain-process IPC (and that IPC/preload bridge), eliminating the old "Restart JoaxClaw / No handler registered" failure mode. Offline hosts use the manual--linksteps in Help → Remote Teams.
[0.7.0] - 2026-06-20
Added
- Remote-gateway local-engine health & model listing. The
joaxclaw-fsplugin now exposesengines.probeandengines.fetch— host-side GETs that check a local LLM engine's liveness and read its model list from the gateway host. Previously, engines on a remote gateway's loopback/LAN showed as unknown because the app can only reachlocalhoston the client machine. The Local LLM Engines panel (Crons) and Settings card now route their probes through the plugin when the gateway is remote, and also discover engines on the host's default ports. Requested URLs are guarded to local-engine hosts only (loopback /*.local/ private IPv4), so the methods can't be used as a general request proxy. NewprobeStatus()routing inlib/localEngines.ts; verified end-to-end against a live gateway. - Saved connections survive a
localStoragereset. Gateway connections are now mirrored to the file-based localstore (~/.openclaw-adjacent~/.joaxclaw/store.json) in addition tolocalStorage, and any missing ones are restored on startup (merged by URL). Previously an ElectronlocalStoragereset (origin change, concurrent instances, profile corruption) could silently wipe them. NewrestoreConnectionsFromBackup()instore/connection.ts.
Notes
- The
engines.probing requires the updatedjoaxclaw-fsplugin on the gateway host. Existing installs need to re-run Install via agent (remote Teams/Processes screen) oropenclaw plugins install --link ./plugins/joaxclaw-fs+ a gateway restart to pick up the new methods; until then remote engines stay unknown* (no regression).
[0.6.0] - 2026-06-19
Added
- Teams & Processes work over a remote gateway via a new bundled gateway plugin,
joaxclaw-fs(plugins/joaxclaw-fs/). Team blueprints (~/.openclaw/teams) and process definitions/runs (~/.openclaw/processes) are files on the gateway host; the app used to read them only over local file IPC, so they were invisible on a remote gateway. The plugin registersteams.andprocesses.(read/write, operator-scoped) over the WebSocket. Both stores now probe for it and use an RPC backend when present (local and remote, incl. items authored by agents on the host), falling back to local files on a local gateway. Covers both features with one install. - "Install via agent" — on a remote Teams/Processes screen, one click hands an agent on the gateway host a self-contained script (the plugin's files travel base64-embedded inside the chat message — no clone, npm, registry, or upload) that installs
joaxclaw-fsand restarts the gateway. You just approve the command. A "Manual steps" path and Help → Remote Teams page document the same install. - Scoped channel routing — agent bindings can now target more than the whole channel: a specific account, a group/peer (by id), a Slack team, or a Discord guild. A binding editor on each channel card builds the
matchand lists existing bindings with their scope; the gateway resolves the most specific match first (peer → guild → team → account → channel → default). - Multi-account channels — add/name/remove additional accounts per channel (
channels.<id>.accounts.<id>) and set the default account, directly from the channel card. Per-account credentials are editable via Edit → Advanced. - Secret-scan pre-commit gate (scripts/check-secrets.mjs) — a dependency-free scanner runs first in the pre-commit hook over staged content and blocks commits containing keys/tokens/credentials (private keys, AWS/OpenAI/Google/GitHub/Slack/Stripe/etc., JWTs,
user:pass@URLs, and the openclaw gateway-token format), with matches redacted in output.npm run audit-secretsscans every tracked file.
Changed
- Client hardware metrics are hidden when the gateway is remote. GPU/CPU/RAM and the local-Ollama model list are read from the client machine (and
localhost:11434), so they don't describe a remote gateway host. The status bar drops the GPU/RAM chips, and the System Monitor + dashboard Resources panel show a short "these are your client machine" note instead. NewuseIsRemoteGateway()selector instore/connection.ts. - Teams and Processes now re-load whenever the gateway (re)connects, so the "install the plugin" notice re-probes and clears itself automatically after an install + gateway restart — no manual retry.
- Polished the team designer (graph editor + Teams view).
- Release workflow now creates the GitHub Release (with
CHANGELOG.mdnotes) in a dedicated job that runs before the platform builds, so the notes can't be raced to an empty body by whichever build finishes first; the Linux/macOS jobs only upload their installer asset.
Notes
- The
joaxclaw-fsplugin ships in-repo and isn't published to a registry; remote installs use the in-app Install via agent flow or a manualopenclaw plugins install --link. Publishing for one-command installs is a possible follow-up.
[0.5.0] - 2026-06-17
Added
- Channel management (Settings → Channels) — configure the messaging platforms the gateway talks on and assign each to an agent. Browse a catalog of ~33 openclaw channels; create one with first-class credential forms for the common channels (Telegram, Discord, Slack, WhatsApp, Feishu, SMS, QQ Bot) or a raw JSON5 editor (incl. env
SecretRefs) for any other. Per-channel live status, enable/disable, Start/Stop/Logout runtime controls, and agent bindings (bindings[]). Newsrc/lib/channels.ts,src/store/channels.ts,src/components/gateway/ChannelsPanel.tsx. - WhatsApp QR pairing in-app — links over the gateway's
web.login.start/web.login.waitRPCs (the same flow the official control-UI uses), rendering the QR as a PNG with auto-refresh and connection detection — no CLI shell-out, and it works for remote gateways. - Auto-reconnect with explanatory UI — adding/enabling a channel makes the gateway reload and briefly drop the control-UI WebSocket. The app now silently retries with backoff and shows a
ReconnectOverlay("the gateway is reloading…") instead of bouncing to the connect screen, then restores the tab you were on. Newsrc/components/layout/ReconnectOverlay.tsx; reconnect state insrc/store/connection.ts. - Local LLM engine detection & isolation — generalized the Ollama-only cron panel to any local engine (Ollama, LM Studio, vLLM, llama.cpp, LocalAI, Jan, KoboldCpp). Engines are discovered from gateway config providers and, on a local gateway, by probing default ports (+ the Ollama
:11435cron convention). Newsrc/lib/localEngines.ts+src/components/crons/LocalEnginesPanel.tsx. Architecture notes: src/lib/LOCAL_ENGINES.md.
Changed
config.patchwrites that shrink thebindingsarray (unbind agent, delete channel) now passreplacePaths: ['bindings'], which the gateway requires for array-entry removal.GatewayView(Settings) remembers its active tab across remounts, so an auto-reconnect returns you to Channels rather than the Connection tab.ollama:probeIPC now takes a full health URL (/api/tagsfor Ollama,/v1/modelsfor OpenAI-compatible engines) instead of an Ollama base URL.- Removed the Ollama-specific
OllamaIsolationPanelfromCronsView.tsxin favor of the generalizedLocalEnginesPanel.
Notes / deferred (see src/lib/LOCAL_ENGINES.md)
- Generalize the Settings "Ollama Endpoints" override card to per-engine URLs (currently Ollama-only).
- Remote-gateway liveness for loopback instances stays
unknown(would need a gateway-side probe RPC).
[0.4.0] - 2026-06-10
Added
- ESLint v10 (flat config) with
@typescript-eslintandeslint-plugin-react-hooks eslint.config.mjs— TypeScript-aware rules;react-hooks/rules-of-hooksis an error,exhaustive-depsand unused vars are warnings- Pre-commit hooks via
simple-git-hooks: runslint → type-check → testson every commit; reinstalls automatically afternpm install lintandtype-checknpm scripts@vitest/coverage-v8for coverage reporting (npm test -- --coverage)- New test file
src/lib/__tests__/process.test.ts— full coverage forprocessParser,processCompiler, andprocessTemplate - Expanded
src/lib/__tests__/team.test.ts— additional branch coverage across blueprint, compiler, and validation coverage/added to.gitignore
Changed
- Test count: 59 → 138
- Statement coverage: 81.98% → 97.22%
- Branch coverage: 61.59% → 90.20%
- Function coverage: 94.64% → 100%
[0.3.0] - 2026-06-09
Added
- Teams — new Teams tab for composing and running multi-agent team workflows
TeamBlueprint(.team.json) as the durable source of truth for team definitions; compiled.mdis a derived artifact- Native conditional branching in the blueprint (
routes,TeamRoute,TeamBranch) with a visual route editor in the Build tab - Skip-style branch routing — a route can jump over intermediate members; validation uses forward reachability (BFS) instead of raw edge counts
- Decision nodes in compiled graphs reuse the
handofftype with multiple conditional outgoing edges — no new runtime node types needed - Revision history for teams — every blueprint save and graph-tab save appends a snapshot (capped at 20); visible in the History tab
- Graph-tab saves are durable: appends revision, persists blueprint + revision file atomically, shows an error banner on failure
- Launch validation (
validateTeamForLaunch) checks controller, members, route references, graph reachability, and smoke-tests the runtime compile path - Export / import teams as
.team.jsonbundles; import preserves compiled graph snapshot when present - Dashboard Teams section — shows running/recent team runs with live status
- Chat input draft persistence — unsent text and attachments are saved per conversation and restored on return
- New app icons across all platforms (Linux, macOS, Windows, iOS, Android); tray icon updated
src/lib/TEAMS.md— architecture reference for the team/process-builder source-of-truth boundary- Process-builder skill updated to distinguish team-compiled process files from standalone process files
Changed
- NavRail now includes a dedicated Teams entry (
UsersRoundicon) saveCompiledDefappends a revision snapshot and persists both blueprint and revision file before updating in-memory state- Graph-tab save failure is now visible (error banner) rather than silent
- Revision list uses stable index keys to avoid duplicates when graph edits share a blueprint version number
Fixed
computeRevisionSummarynow detects graph-edit and blueprint-regeneration transitions- Process-builder skill validation checklist clarified: incoming-edge rule applies to standalone processes only; team branching graphs with skip routes are exempt
[0.2.0] - 2026-06-08
Added
- macOS universal DMG build (Intel + Apple Silicon) via GitHub Actions
- Unsigned — see README for first-launch instructions
Changed
- GitHub Actions workflow now builds Linux DEB and macOS DMG in parallel
[0.1.0] - 2026-06-08
First public release.
Added
Chat
- Streaming assistant responses with reasoning block (auto-expands while streaming, collapses on completion)
- Tool call visibility with per-call status, duration, and expandable input/output
- Inline image, video, and audio rendering via markdown link syntax
- Audio player with format badge and source icons (Obsidian vault, Google Drive, Dropbox, remote)
- Image attachment support — paste, drag-drop, or file picker; images forwarded to vision models
- Voice recording input
- Message feedback (thumbs up / down) — persisted to
~/.openclaw/feedback/feedback.jsonl; thumbs-down writes a memory note the agent reads next session - Smooth scroll that tracks text streaming, tool call additions, and reasoning updates
- Instant jump to bottom on conversation load (no animation)
/compactslash command for context compaction
Dashboard
- Quick-send card with agent picker (fixed dropdown clipping in scroll containers)
- Recent conversations list with last message preview
- Live system metrics panel (CPU, RAM, GPU via
systeminformation+ Ollama API)
Agents
- Agent grid with model, emoji, session count, and last-active time
- Subagent relationship display
Sessions
- Sortable, filterable session table
- Load any past session into chat for replay
CRON Jobs
- Job list with schedule description, model, last/next run time, and run history
- Inline run history with status, duration, token usage, and link to conversation
- Ollama Isolation panel — detects both Ollama instances (
:11434main,:11435CRON), shows architecture diagram and contention warning, auto-expands when setup is needed
Processes
- Visual process graph builder with node types (agent turns, conditions, loops)
- Live execution monitor
Models
- Provider management (Ollama, OpenAI, Google, and more)
- Per-model token usage and cost tracking across sessions and CRON runs
- Model picker with live Ollama VRAM status
Gateway Config
- Monaco JSON editor for
openclaw.json - Gateway start/stop/restart controls
- Live Ollama model list with loaded status and VRAM usage
Obsidian Integration
- Vault detection and setup wizard
- File browser and graph view
- Memory panel
Settings
- Dark / light / custom themes with full CSS variable control
- Icon family selection (Lucide default)
- Status bar configuration
Infrastructure
- Linux
.debpackaging via electron-builder - GitHub Actions CI — builds and releases on version bump to
main - Ollama CRON isolation skill (
~/.openclaw/skills/ollama-cron-isolation/SKILL.md) - Updated
display-mediaskill with audio player syntax and source badge documentation