Release notes
Foxl changelog
Release notes for Foxl, including new features, improvements, and bug fixes by version. Foxl Desktop, Foxl Code, and Foxl Notes ship under one version number, so every entry below covers all three.
Open Settings › General in Foxl to check your installed version. Updates install themselves; download the latest build if you are starting fresh.
Improvements
- Refined Agent, Notes and Code with a shared product menu, clearer sidebar groups, consistent spacing, and matching title-bar and sidebar colors in dark mode. Chat keeps its centered composer and rounded workspace surface.
- Settings uses a more compact navigation rail. Click a keyboard shortcut to change it directly, with clearer recording and conflict feedback.
- Files uses a compact toolbar and a complete “Search files” placeholder. Notes on phones keeps sharing and copying together in the top overflow menu.
Fixes
- Signing in again or switching accounts while tokens are refreshing no longer lets a late response sign out the new session or restore the previous account's data. Automatic refresh and concurrent request sharing continue within each login.
- Windows: a background service that crashes now really does come back, and the previous release's claim that it did was wrong. v0.7.8 said restart-on-failure was "requested rather than working" and asked to be checked on a real Windows desktop. It has now been checked, on a real Windows Server 2022 machine, and the honest answer is that the setting it relied on does not do this job: Windows records a background service whose process exits - for any reason, with any exit code - as having completed successfully, and does not start it again. Four different ways of killing it were tried and none came back. The setting is not broken and it is kept, because it does cover the case it is for (the service failing to start at all - a moved app, a deleted launcher - which retried three times a minute apart, exactly as documented). What it does not cover is a crash. The service is now also asked to re-check itself every minute, with Windows' own rule that a run is skipped while one is already going. Measured on that machine: a killed service was back 30 seconds later, and a healthy one was left alone across the check - so "keep running when closed" now survives a crash without waiting for the next Windows sign-in, which is what it was supposed to do since v0.7.8.
- The Chrome extension can use the automatic connection again - it has not worked in any released build since v0.7.4. That release replaced the copy-a-code step with Chrome's own native messaging, so the extension is supposed to be vouched for by the browser and connect with nothing for you to carry. On every packaged build, on every platform, the piece Chrome starts was looked for in the wrong folder, so Foxl reported the connection as unavailable on each launch and the extension quietly fell back to the older path. Found by reading the app's own log on two machines - the same line on every start going back at least to September 6 - and confirmed against the installed files on both. Nothing you set was wrong and nothing needs redoing: install the update and the browsers are set up again on the next launch.
- The iOS sidebar's native Liquid Glass button follows the page transition without jumping vertically or disappearing offscreen. The material and touch target move together.
- Foxl Code no longer repeats the “Needs you” heading. Development reloads reuse the React root, preventing duplicate panels and detached click handlers.
- Saved Notes transcripts open at the beginning, and the phone login form keeps its fields reachable while the keyboard is open.
- Native iOS and Android origins can reach the Code service through its CORS checks; protected routes still require authentication.
Features
- GPT-6 Astra now runs on Amazon Bedrock - on Foxl credits and with your own AWS account. It was reachable only through a ChatGPT subscription or an OpenAI API key; AWS made it generally available on September 8, 2026 and it is now offered under Settings > Model & provider > Amazon Bedrock, alongside Claude and the GPT-5.6 tiers, and on the hosted catalog for Pro and Ultra plans. 1,050,000-token context, 128,000 max output, and reasoning, tool use and streaming all work. With your own AWS account it needs no region setting - it works from us-east-1, us-east-2 and us-west-2.
- The reasoning effort you pick is now the one GPT-6 Astra uses on Bedrock. Effort levels reach the model on that transport for the first time: previously any non-Claude Bedrock model was sent a single fixed reasoning level, so choosing Low or Max changed nothing while the picker went on showing your choice. Astra also cannot turn reasoning off at all, so switching thinking off now sends its lowest level rather than nothing, which used to mean the server quietly chose for you.
Fixes
- The agent finds your earlier conversations again when it asks in a full sentence. Searching your past chats used to require every single word to appear somewhere in one message, so a question phrased naturally - "지난주에 결정한 릴레이 설정", "what did we decide about the homebrew cask" - came back empty even when the subject had been discussed a dozen times, because one inflected ending or one filler word was enough to rule everything out. It now starts strict and then drops the least distinctive word until it finds something, keeping the rare words that identify what you meant, and it tells the agent which words it actually used so a partial match is never presented as an exact one. A search for something you genuinely never discussed still comes back empty rather than offering unrelated conversations. Searching a whole question is also much faster - a six-word question measured 4.9 seconds before and 0.23 seconds now. The search box in the title bar is unchanged.
- A link to another app in a reply opens that app again.
obsidian://,vscode://,slack://,tel:,mailto:and the rest of the app schemes Foxl supports had been clickable since v0.5.14, and from v0.7.0 clicking one did nothing at all: the desktop window had been narrowed to open web addresses only, so the link still rendered underlined with its scheme in the tooltip and the click was refused in a log line nobody reads. The window now opens exactly the set the chat already renders as a live link - editors, note apps, meeting and call links, calendars - and nothing else. The Open links in other apps switch in Settings still limits replies to web and mail addresses when it is off, and the schemes that could run code or read your disk are refused either way. - Windows: "Restart service" tells you the truth, and reconnecting can no longer uninstall the service. Pressing Restart service reported success while restarting nothing - it read the exit code of the request, not whether the process had been replaced. It now checks that a NEW service really took over, and it only believes the fallback signal in the one case that signal is about (Windows reusing the process id it just freed) rather than treating a freshly restarted child as proof the service above it restarted. Separately, pressing Reconnect could reach a repair path that DELETES the background service and its saved sign-in - so a button you press because your phone cannot see the machine could take the machine offline whenever the app is closed. That path now reports the problem and changes nothing. The service is also asked to register with a restart-on-failure rule so a crashed service comes back without waiting for the next Windows sign-in. That request is not confirmed yet, and it is worth being precise about what is and is not known: Windows' own parser reads the settings back correctly (retry three times, one minute apart, no time limit, keep going on battery), and no registration has ever been observed. The machine the automated check runs on has no signed-in user, and Windows will not register a scheduled task for it - measured three times, two different ways. So treat restart-on-failure as requested rather than working until it has been seen on a real Windows desktop.
Features
- The terminal workspace can be arranged, not just split. The Terminals actions menu has a Layout section with five named arrangements: Columns and Rows put every terminal in equal columns or equal rows, Main on top gives the terminal you are in the top of the screen with the others sharing a row underneath it, Main on the left does the same down the side, and Grid lines them up in level rows. None of them opens or closes a terminal - they move the panes you already have, so every session keeps its scrollback and you can click through all five looking for the one you want. Main on top is the shape that was previously unreachable: splitting the same pane repeatedly can only ever make a line.
- A Foxl Code terminal shows what it is spending, in its top-right corner, as it spends it. Context, tokens sent and received, and the coding CLI's own cost figure - the same readout and the same formatting as Foxl Agent's titlebar, because it is the same measurement in a different window. The numbers are the ASSISTANT'S OWN: a coding CLI running in a terminal puts no usage on the wire, so Foxl reads the accounting the CLI journals for itself rather than estimating it. Re-opening a pane shows the last reading immediately instead of waiting for the agent's next turn.
- On a subscription it shows subscription usage instead of a price. A plan is not metered in dollars, so a dollar total would be a number that looks like a bill and is not one. With codex you get the plan name and how much of each quota window is gone - the five-hour block and the week - with the reset time on hover, and the figure turns amber at 75% and red at 90%. On a Claude subscription you get the plan and the tokens: Claude Code publishes no quota consumption anywhere, so Foxl says what it can measure and does not guess the rest.
- A Kiro terminal shows how many credits you have LEFT, not how many you have spent. Kiro bills against a plan measured in credits, so that is the number the corner shows: the balance, the percentage of the plan gone, the plan's own name, and when the cycle resets - plus, on hover, how many credits this particular terminal has used. It is read from Kiro's own account rather than estimated, and the unit label comes from Kiro too, so a plan that meters something else reads correctly. Deliberately not a dollar figure: a plan is not billed in dollars until it overruns, and an overrun is shown as an overrun. One caveat, stated because it is the difference between a missing readout and a wrong one: whether Kiro accepts a cloud terminal's API key for this particular lookup has not been confirmed with Kiro yet. If it does not, the balance is simply absent - no figure is ever invented - and the refusal is logged with its status so it can be told apart from an account that genuinely has nothing to show.
- opencode and Hermes terminals show their tokens, read from the accounting each CLI keeps for itself, with opencode's own cost figure alongside. A Hermes session running on Bedrock shows no price, because Hermes records that it did not price that session - a $0.00 on work that really cost money is the one number worth refusing to print.
- The readout appears for Claude Code, codex, Kiro, opencode and Hermes. A Cursor terminal shows nothing, and that is now a measured fact rather than a gap: the Cursor CLI writes no session record at all and offers no usage command, so there is nothing to read and nothing to ask. A wrong number in the corner of the screen is worse than no number, because there is no way to tell which one you are looking at. In a narrow or split pane the readout sheds its least useful segments instead of quietly cutting one off - the credit balance is the last thing to go, since it is the only figure that answers whether you can finish the afternoon.
Changes
- Closing a terminal in Foxl Code closes the window, and stops nothing. Until now, if the terminal was one you had opened here yourself, its X also cancelled the agent running in it. The reasoning was that such a pane IS its session, so closing it left a cloud machine running with nothing on screen attached to it - but X is the reflex gesture for "get this off my screen", it sits next to the zoom button, it asks for no confirmation, and it was ending an agent mid-edit with no way back. Stopping a runtime is now only ever what a control that says "Stop" does: Stop runtime in the pane's own menu, or Stop on the task row. Switching to the Conversation tab, narrowing the window and navigating away from a task no longer stop anything either, for the same reason. A closed pane still stays closed, and a run you left going is still on the Tasks page and still reopenable from "Watch a run". A LOCAL terminal is unchanged: its shell ends with the pane, because otherwise the next pane adopts the session you just closed and it comes back with its scrollback.
- The desktop app is on a supported Chromium again. It was built on Electron 41, which stopped receiving security fixes on 2026-08-25, so the browser engine inside the app - the thing that renders every message, note and provider sign-in page - had been six Chromium versions behind and getting further behind each week. It is now on Electron 44 (Chromium 152). This raises the macOS requirement to macOS 13 (Ventura), because Chromium itself dropped support for macOS 12; a Mac on 12 will refuse the new version by name rather than installing it and failing to open.
- Shared search no longer reads everyone's documents to answer one person's query. It walked the whole shared corpus before narrowing to what you are allowed to see, so it got slower as other people shared more. It now reads only the documents you can see - measured, a search that examined 6,000 rows examines 50 - with the same results in the same order. This release also lays the groundwork for removing a duplicate copy of every shared document's text; that removal, and the storage it frees, comes in a later release.
- A Foxl Code terminal reconnects itself when its machine has gone to sleep. Opening a task whose machine had been recycled used to show a card saying the session stopped, with one Reconnect button to press. The terminal now brings it back on its own, showing only the standard Foxl loading blur while it does - no card, no button and no wording. It retries a couple of times if the runtime refuses, and if the session genuinely cannot be resumed it says so instead of leaving the blur up. It only does this when the machine is really gone: a task you stopped yourself, or one that has finished, is left alone and keeps its Reconnect button, so nothing restarts work you ended.
Fixes
- A browser instruction is addressed to one connection now, and only that connection can answer. Every instruction Foxl sent to your browser - the page it was opening, the text it was typing - was sent to every program connected on the extension's channel, and the first answer back was the one Foxl believed. So a second connection could both watch and answer, and Foxl would work from a page it had never really seen. If the connection drops mid-instruction, Foxl says so instead of waiting thirty seconds.
- The reasoning effort slider no longer shows a dark rim on the left of its handle at the lowest level. The filled part of the track was drawn up to the middle of the handle even at Low, where it should be invisible, and a sliver of it escaped around the handle's left edge. The highest level was never affected.
- The model picker no longer slides sideways while you drag the effort slider. The model chip in the composer carries the effort next to the name, so the chip changed width as the level changed - and because the picker opens aligned to that chip, it jumped up to 23px left or right under the pointer. The effort now sits in a slot as wide as the longest level, so neither the chip nor the open picker moves.
- "Equalize pane sizes" now makes the panes equal. It set every divider to the middle, which is not the same thing once you have more than two terminals: three panes opened with two presses of Cmd+T came out 50% / 25% / 25%, so the pane you started in kept half the workspace and the command whose whole job is evening things out was what put it there. It was only correct at two panes, or at four split evenly. Every terminal now gets the same area whatever shape you built.
- A generated image is no longer cut off in the chat when you reopen the conversation. A tall image (the 1024x1536 and 1536x1024 sizes especially) showed its top three quarters with white underneath, and only after a reload - the picture looked right while it was being generated, which is why it read as a bad generation rather than a display bug. The preview stored with the message was slightly too large for the transcript's own size limit and was being trimmed part way through the image data, which a browser draws as far as it got and then leaves blank. Previews are now sized to fit that limit, and an image that still cannot fit is left out rather than shown half-drawn. Images already in your history are served whole again too, so old conversations look right without regenerating anything. One trade comes with it: the zoom view shares the thumbnail's copy, so zooming a tall generated image is slightly softer than before. The full-resolution file is still on disk, and Reveal in Finder or Save Image As open that.
- "Reveal in Finder" on a generated image opens the right folder again. The same trimming took the file path with it, so the menu item pointed at a path that did not exist and did nothing at all when clicked, with no error.
- Numbered lists no longer lose their tens digit. In a chat reply, a numbered list of ten items or more had the left edge of every two-digit number cut off - "10." rendered as "0.", "11." as "1." - on the phone and on the desktop, while the text beside it was fine. The number is drawn outside the text column and the column trims what sticks out, so there was not enough room for a second digit. Lists now leave room for three.
- The encrypted tunnel now refuses a frame that was replayed, reordered, reflected or aimed somewhere else. Encrypting a frame stopped nobody from RE-SENDING it, and for a keystroke frame that means re-typing into your shell; nor from pointing one at a different terminal. Each frame is now tied to its direction, its position in the stream and the terminal it names. A frame that arrives out of turn is dropped and counted; one that has actually been tampered with is dropped, counted and reported as an error. Either way your other devices keep working. This is about a frame surviving in one piece, not about who can read it, and it holds against anyone who can copy frames off the network rather than against something that can rewrite the initial handshake. Older desktops and phones keep working exactly as before, and get none of it until both ends have been updated.
Features
- Foxl Code has its own reasoning depth. The model picker in Foxl Code now carries a Reasoning switch and the same Low/Medium/High/xHigh/Max effort levels Foxl Agent has, and it is a separate setting - changing how hard Foxl Code thinks no longer changes Foxl Agent, which is what it used to do (Foxl Code simply read the Agent's setting and had no control of its own). Models whose reasoning cannot be turned off show "Always on" instead of a switch that would not do anything. The control appears only for models served by your own computer, because those are the turns it can affect.
Changes
- Foxl Code's Work board is three lanes now, and they are grouped by who has to move next. It was five - what needs you, what is not started, what is working, what is done, and the activity log - and two of those five were wrong about their own contents. A pull request your agent has FINISHED and left for you counted as "Working", so it sat under a spinning icon that turned for as long as the PR waited. On a real account that was two days, over a change that was approved with its checks green, while the "Needs you" lane counted 24 other things and not that one. It is a decision, so it is in Needs you now, as a card that says "Waiting for your review" with Diff and Merge on it. The lanes are Needs you, In flight (an agent may still act on these - nothing here is waiting on you) and Done. Nothing animates any more: each row shows how long it has been running instead, which is the number that tells you a run is stuck. A run that is paused waiting for your answer says so rather than reading as busy. Done stopped being a wall of cancelled runs. It was 39 rows of which 37 said "Cancelled", and every one of them was titled with the repository name, so the column read as the same row a dozen times. Cancelled runs are folded behind one line you can open; merged and failed stay open; and a row without a title now says what it is about - "Pull request #12" - rather than repeating its repository, which is already on the line below. Not started and Activity are still there as tabs, with links to them under the board. Both want the full width of the page rather than a fifth of it: Not started has to ask which repository first, and the activity log runs into the hundreds.
- A button means the same thing on every card in Foxl Code. The cards had been unified once already, but only their shells were - each one still chose how its own buttons looked. One Merge was a solid button, another was grey, another was a plain word with no outline at all, and on the suggestion cards it depended on the order the server happened to send the actions in. So the most irreversible thing on the page, merging someone's code, looked different depending on which card you reached it from, and on one card it looked like a link. Now every card asks one place. Each card has exactly one solid button - the thing that is actually recommended for that card - and everything else is outlined. On a pull request your agent just finished, the solid one is Diff, because nobody has read the change yet; Merge sits beside it. On a pull request that is already approved, Merge is the solid one. On a red build it is Fix it. When a card has nothing to recommend, it says so by having no solid button rather than promoting whatever is first. Two smaller things came with it: a button waiting for its second press is red on every card now (it used to go red on some and black on others, for the same write), and buttons in the activity log are the same size as buttons on the cards.
- Reasoning effort is a horizontal slider in the model picker on desktop. It used to be an "Effort Low >" row that expanded a vertical list, so picking a level took two clicks and said nothing about the fact that the levels are an ordered scale. It is now one track with a stop per level and the current level named above it - drag it, click anywhere on it, or use the arrow / Home / End keys. Only the levels the selected model accepts are offered, exactly as before. The phone sheet keeps its one-tap pills.
- Choosing a model no longer closes the picker on desktop. Picking a model and setting its effort is one decision - the levels are per model, so they can only be set after the model is - and closing on the first half meant a second trip through the trigger for the second. The check mark moves to the new row and the effort control re-renders with that model's own levels. The phone sheet still closes on a tap, where it is also one of the three ways out of a sheet that covers the screen.
Fixes
- Merge asks twice on every card in Foxl Code, including the new "waiting for your review" one. That card is the one this release moved finished pull requests onto, and it was the only place in the product where a single click merged to a default branch - every other Merge on the board arms first and needs a second press. Its Merge sat a few pixels from the filled Diff button the card asks you to press first, on a change nobody has read yet. It now turns red and says "Confirm merge" for the second press, like the others, and disarms itself after four seconds.
- A pull request with failing checks no longer looks ready to merge. The same card showed only "The agent finished and opened a pull request", so a red build or a reviewer asking for changes was not on the row at all - and that card is what you see for those pull requests, not a separate one. It now leads with "Checks failed" or "Needs changes" when either is true, in the same words the other lanes use.
- Foxl Code's model picker no longer names a reasoning depth the turn does not use. Until you choose one, Foxl Code follows your desktop's setting - but the picker filled the gap with the model's own default instead, so on a stock setup it highlighted High while the turn ran at medium, and it showed reasoning switched on for people who had switched it off. It reads the desktop's real value now, so what it shows is what runs.
- Picking your Claude subscription for Foxl Code is no longer billed to your AWS account. A model chosen under Claude Code (OAuth) and the same model under Amazon Bedrock are one model on two accounts, and the part of Foxl that decides which one serves a request treated them as identical - so with Foxl Agent on Bedrock, choosing the subscription in Foxl Code silently ran on Bedrock and charged AWS, with nothing on any screen saying so. The transport is now part of that comparison, so the account you picked is the account that answers.
- "Read all" and "Clear all" are gone from Foxl Code's activity log. Neither did what it looked like. There is no read state to mark on a log of things that already happened, and both only wrote to the browser you pressed them in - so "Clear all" hid rows the server keeps sending, on one device, with nothing cleared anywhere else. Hiding a single noisy row still works. The repo filter now applies to the log like it does to every other tab.
- The Work board no longer stretches past the bottom of the window when a lane is long. With 22 cards in the first lane the board grew to roughly four screens tall, the page did not scroll, and the links under it could not be reached at any window size.
- Image generation works again. On v0.7.5 every "draw me ..." request failed with
The 'gpt-5.4' model is not supported when using Codex with a ChatGPT account. That release correctly removed GPT-5.4 from the model picker - OpenAI had stopped serving it to ChatGPT accounts - but the image tool carried its own separate copy of that model name, so the picker came out clean and the image tool was left calling a model that no longer exists. It now uses GPT-5.6 Sol, and the model name lives in one place instead of two, checked by the same gate that keeps removed models out of the picker. Verified by generating real images: five of the six models the ChatGPT account offers can carry an image request and GPT-5.4 is the only one that cannot. - Foxl Code chat failed with "Language model stream error: Bad Request" whenever its model came from a different provider than Foxl Agent's. Every message in Foxl Code ended in that one line, which named no model, no provider and no reason. The cause was that a Foxl Code turn was assembled from two unrelated decisions: the MODEL came from Foxl Code's own picker, and the PROVIDER came from whatever Foxl Agent happened to be set to. So with the Agent on ChatGPT and Foxl Code on Claude, a Claude model id was sent to ChatGPT's endpoint, which serves no Claude model - and the upstream refusal was wrapped in a way that discarded everything useful about it. Foxl Code now decides the model and the provider together, against the same catalog its own picker is built from, so the two halves can no longer disagree. If the model you picked genuinely cannot run on this computer, the reply says which model answered instead rather than failing.
Changes
- Claude Fable 5.1 is available under Claude Code (OAuth), and the effort control on that provider now reaches the model. Fable 5.1 (released September 1) joins Opus 5, Fable 5, Opus 4.8 and Sonnet 5 in the Claude Code (OAuth) model list. Two things about that provider had to change for it, and both were found by sending real requests rather than by reading. First, Anthropic decides which models a Claude Code client may use from the client's version: the version Foxl identified itself as (2.1.75) was refused for Fable 5.1 with "version 2.1.251 or newer is required", so Foxl now identifies as the current CLI, 2.1.261. Opus 5 was re-checked on the new version, with and without tools, and still runs on the subscription. Second, the effort level you pick (low through max) was being removed from every Claude Code (OAuth) request before it left the app, on the old belief that the subscription would push a request carrying it onto pay-as-you-go billing. Measured on Opus 5, the subscription accepts all five levels sent the way the official CLI sends them, so the effort you choose now applies on this provider too. One caveat, stated plainly: on a Claude Pro account without extra-usage credits, Fable 5.1 answers "Usage credits are required for this model." It is not part of the subscription and is billed against usage credits when your account has them. That reply is now shown at once rather than after a minute of automatic retries, which is what a rate-limit-shaped error used to get.
- The browser extension connects through Chrome now, and the pairing code is gone from Settings. Foxl used to print a 64-character code in Settings > Web Access for you to copy into the extension, because the extension runs in Chrome's sandbox and a human was the only channel a secret could travel through. Foxl now installs a small connector that Chrome itself starts, and Chrome only lets the Foxl extension use it - so there is no code to copy, nothing secret is stored in your browser, and there is no longer a local port for another program on your machine to knock on. Settings > Web Access shows which browsers are set up instead, with a Reinstall button for when you add a browser or move the app. Requires the Foxl extension 0.7.2 or newer; an older extension keeps working over the previous connection, and Settings says so when that is what is happening.
Fixes
- The chat box on the desktop suggests something from YOUR work again, not three generic lines. The grey prompt inside the message box (press send on an empty box and it sends that prompt) had been rotating the same three placeholders forever - "Summarize a document or link", "Research a topic and cite sources", "Automate a task on a schedule" - while the phone and web app, asking the same desktop over the relay, got suggestions drawn from your profile, your recent sessions and your recent chats. The desktop was skipping that request entirely. It now makes it, so the placeholder and the empty-chat chips both read like the work in front of you. Nothing new is displayed: this is the same placeholder, with better text in it.
- A chat opened with "Open beside" can be scrolled. The side pane laid its transcript out with no height of its own, so the whole conversation was drawn at full length and everything past the bottom of the card was cut off rather than scrolled - a wheel, a drag and a keypress all did nothing. Only the newest messages were reachable, which is the opposite of what you open a second chat to read.
- Your phone no longer notifies you that your phone connected. "Device connected" is a fact about your computer - something reached it - so it belongs on the machine it is about. It was being fanned back out over the relay to every phone and browser tab as well, including the one whose own connect had just caused it, so bringing the iOS app to the foreground raised a banner announcing itself. The desktop app still shows it, unchanged, and the Notifications setting still governs it there.
- The unread count on the notification bell no longer has a pale ring around it. The red dot in the sidebar carried a 2px halo in the sidebar's own colour to stop it touching the bell, which in light mode read as a white band drawn round the circle. It is a solid dot now, matching the badge in the desktop title bar.
- "Sign out everywhere" now stops notifications too, not just sessions. v0.7.3 shipped that control with a caveat printed in its own dialog: a device you never sign in on again kept receiving previews of new messages, because the relay picked notification targets by account alone and the signed-out device's registration was still on file. So the one case the control exists for - a phone that is lost or stolen - was the case it did not cover, indefinitely. New notifications now stop reaching a device as soon as the account signs out everywhere, and start again by themselves the next time you sign in on it. Notifications already delivered are still not recalled, which is the only thing the control cannot do, and the dialog now says just that.
- The reasoning effort picker now works for ChatGPT and Claude Code models. Selecting a ChatGPT (GPT-6 Astra, GPT-5.6, GPT-5.5) or Claude Code model showed a bare Reasoning on/off switch and no Low / Medium / High / xHigh / Max row, while the same models under Amazon Bedrock showed all of them - so on those two subscriptions there was no way to ask for more or less thinking. Both now offer the levels the model actually accepts.
- GPT-6 Astra no longer shows a reasoning switch that did nothing. That model cannot run without reasoning - asking it not to is refused outright - so the row reads "Always on" and the effort levels stay available. Turning the old switch off did not stop it thinking; it quietly replaced your chosen effort with the lowest one, so picking Max and leaving reasoning off ran at Low.
- GPT-5.4 is gone from the ChatGPT model list. ChatGPT subscriptions are not served that model, so every message sent with it failed. The list now carries only models verified to answer on that connection.
- A long ChatGPT conversation compacts at the right point. GPT-6 Astra reported a 1,050,000-token context while this connection serves 272K, so the context meter read low and compaction started far too late - a long session could hit an upstream limit instead of compacting. It now reports the real window.
Features
- Foxl Code's Work page is a board on a wide screen, and you can switch it off. On a desktop window 1280px or wider it now uses the full width and puts all five questions side by side as lanes - what needs you, what is not started, what is working, what is done, and the activity log - so the whole state of your fleet is one glance instead of five clicks. Each lane scrolls on its own, so a long activity log no longer stretches the page past four empty columns. Every card, button and menu is the one you already had; only the layout changed. A Board / List switch sits at the top of the page. Pick List and you get the single full-width page you had before, tab strip and all - which is still the better way to read a long activity log or page back through what is done. The choice is remembered, so the page opens the way you left it. Phones and tablets are deliberately untouched: below 1280px the tab strip and single list are exactly as before, which covers every iPhone and every iPad in portrait. Narrow a desktop window past that point and the tabs come back on their own. The "Needs you" side panel steps aside on this one page while the board is showing, because its list is the board's first lane - the same rows, right next to it - and it was taking 360px of the width. It is unchanged on Tasks, Repos, Files, Runtimes and Schedules, and it returns on Work the moment you switch back to List or narrow the window. One smaller fix in the same area: with a repository filter set, the activity lane's heading counted every event in the store rather than the ones it was showing, so the number disagreed with the list underneath it.
Features
- "Sign out everywhere" is in Account > Sessions. One control now ends every session on every device, including the one you press it on. There was no such control before: you could only sign devices out one at a time, and a device signed out that way kept working for up to twelve hours because signing it out did not reach the credential it was already holding. Both are fixed, and the sign-out takes effect on the very next request. Note it ends SESSIONS: a device you do not sign in on again may still show previews of new notifications, and the dialog says so.
- Foxl Code shows a PDF, a Word file, a spreadsheet and a deck the way Foxl Agent does. The Files page rendered an image and offered to download everything else, so the four formats a coding agent most often leaves behind as a report could only be opened in another application. It now mounts the same viewer the Agent's Files page uses: a PDF pages, a
.docxlays out properly, an.xlsxbecomes a table, a.pptxshows real positioned slides, and audio and video play. A file shared with your workspace has a Preview button beside Download for the same reason. Files under Tasks, Computer and Session are served as text by the machine that made them, so a document there has no original bytes to show; the page now says that plainly and offers the download, instead of printing the file's decoded contents. - Files in Foxl Code opens in a real editor, and shared documents can be edited there. The Files page showed a file as coloured, numbered text you could only read. It now mounts the same editor Foxl Agent's Files page uses - the VS Code core, with find and replace, folding, bracket matching and multiple cursors - and a document shared with your workspace can be typed into and saved, with Cmd+S (Ctrl+S on Windows and Linux), an unsaved marker beside the filename, and a Save button that appears the moment you have changes. A shared document still opens as formatted prose, because that is how a runbook is read; the Edit button in the header is the way into the editor. Files under Tasks, Computer, Storage and Session stay read-only and now say so, because the agent that produced them is what writes them - the editor there is for reading with real tools, not for typing. A save that the server refuses now tells you why and keeps your text, instead of quietly settling back as though it had worked.
- An .html file in your workspace opens as a PAGE now, not as source. Open one from the workspace panel and it renders the way a browser renders it: the stylesheets, images, fonts and scripts it references load from the paths the file itself names, so a report with a
style.cssbeside it looks like the report. The markup is still one click away on a Source tab, and there is a Reload button for a file the agent is still writing. On app.foxl.ai and on your phone nothing can fetch a file off your desktop by URL, so the linked files are brought over the tunnel and folded into the page instead. That covers stylesheets, images and scripts; anything the page fetches LATER by running its own code cannot be brought in ahead of time, and whatever could not be loaded is named above the preview rather than left as a silent gap in the page. The preview runs sandboxed either way, on an origin of its own, with no access to your account, your session or any other file on the machine.
Changes
- GPT-6 Astra is available on the ChatGPT sign-in. OpenAI's new flagship, released today, appears in the model picker as GPT-6 Astra when you are signed in with a ChatGPT account, with the five effort levels the model accepts - low, medium, high, xhigh and max - each confirmed against OpenAI's endpoint before it was listed. Astra cannot switch its reasoning off: asking it for "none" fails the request, so Thinking off runs it at the lowest effort instead of failing your turn. On this sign-in the context window is the 272K OpenAI serves there, as with GPT-5.6, and the cost estimate uses OpenAI's published API prices. Three defects on the same path were fixed on the way. The effort you chose could be dropped before it reached OpenAI, for any model the underlying SDK did not recognise by name: on the version installed when Astra launched, every Astra turn would have run at OpenAI's default effort while the picker said otherwise, and the request still succeeded, so nothing looked wrong. The SDK's own fix for that shipped separately, and Foxl now requires the fixed version rather than hoping for it, as well as telling the SDK directly that every model on this sign-in reasons. "Max" was quietly sent as "high" for every GPT model on this sign-in; it now goes out as picked. And the sign-in's model discovery was pinned to an old client version, which is why it appeared to list nothing newer than GPT-5.4 Mini. Astra is not on the Bedrock providers yet. OpenAI says it is coming to Bedrock; it will be added there once a real call has confirmed it.
- Foxl has its own typeface now. The app, foxl.ai and docs.foxl.ai all render in Geist for body text and UI, Instrument Serif for display headings, and JetBrains Mono for code - self-hosted, so they look the same on macOS, Windows and Linux instead of falling back to whatever each OS ships. Korean text uses Pretendard in the app; it is loaded only when Korean is actually on screen, so an English session downloads none of it. Until now body text was the system font, which meant the product looked like a different application on every platform and unlike its own website. The Latin faces add about 148 KB across all three families.
- The new-chat screen on desktop and tablet is the mark and the question again. v0.7.1 shared the phone's whole zero state to every window width, which put three starter prompts ("Summarize a document or link", "Research a topic and cite sources", "Automate a task on a schedule") under the greeting on the desktop app, app.foxl.ai and code.foxl.ai. They were written for a phone, where the composer is small and tapping a phrase beats typing one; on a desktop the composer is already large and focused, so they spent the most valuable strip of the first screen on canned text. They are unchanged on the phone, which is where they belong. The Foxl mark stays on every width.
- The new-chat greeting now uses Foxl Code's task-composer type. "What can I help you with today?" is set in the same light weight and leading as the identical line on Foxl Code's Tasks screen, so the three surfaces that ask this question ask it in one voice. Same size as before on a desktop.
- The four Foxl Code request limits added in v0.7.1 are OFF again, because the numbers were too small. A chat turn was capped at 20 a minute and 300 an hour, forcing the autonomous loop's next run at 10 and 60, adopting a GitHub installation at 5 and 10, and typing into a cloud terminal at 600 a minute - and every one of those is reachable on a busy afternoon, so the cap you would actually meet was your own, not a runaway script's. None of them applies now: nothing limits how many requests you send. The machinery stays in place and each limit can be switched on per environment with a single setting, at a number chosen then rather than the tight ones, so if a looping client ever does become a problem the answer is a setting change and not a release. Two details worth knowing if you operate this: a limit of zero is refused rather than obeyed, since it would admit nothing while looking exactly like a bug, and the service's health check now reports which limits are actually in force - including any setting that was rejected - so a change that did not take effect says so instead of looking applied.
- The Files tree names its root. The five shelves at the top of the list (Tasks, Computer, Storage, Session, Shared) sat under nothing, so there was no indication of what the tree as a whole was or any way back to it once a file was open. There is now an "All files" row above them, and clicking it returns you to the whole list.
Fixes
- Remote terminals in Foxl Code take input again. A cloud terminal came up, painted the assistant's screen, and then ignored every keystroke for the rest of its life - on Claude Code, Kiro and Codex alike. This is the defect behind every "the terminal starts and then freezes" report since v0.6.0, and it was never in the terminal: keystrokes were being sent to the wrong machine. Each cloud terminal streams its output over one long-lived connection to the microVM running it, and keystrokes were sent as separate requests addressed to the same session. Measured on production: while that streaming connection is open, a second request for the same session is served by a *different* microVM - one with no terminal in it - so every keystroke was answered "this session has gone to sleep" while the real terminal sat there working. The screen-reading request missed in the same way, which is why the pane could also report that the machine was still starting for as long as it was open. Keystrokes and resizes now travel the other way round: the machine running your terminal asks for them, so there is no second address to get wrong. A keystroke typed while the assistant is still starting is still held and delivered in order once it can read, and a terminal whose machine really has gone away still offers Reconnect.
- Your Claude subscription tokens no longer appear in a command line. Refreshing them wrote the new pair back to the keychain in a way that put both the access and the refresh token where any program on your Mac could read them for the moment of the write. They now travel on a private channel, and the write is verified rather than assumed.
- A ChatGPT sign-in that has been open for a while no longer fails its first message. The desktop kept using an access token past its expiry and only noticed when the request was refused; it now refreshes before sending.
- Signing in to Codex with an API key no longer offers ChatGPT subscription models. They were listed and then failed on first use. An API key belongs to the OpenAI provider, where it already works.
- A custom tool parameter containing
$&is passed through as typed. Certain characters in a value had part of the surrounding command pasted into them. - A skill that requires a program by its full path can be satisfied. Such a requirement could never be met, on any machine, however the program was installed.
- Daily memory entries are dated the day they were written. Near midnight the file and the date written inside it disagreed by one day.
- Notes usage is recorded against the model that answered. With Notes pinned to a different provider than the Agent, a Notes reply was filed under the Agent's provider and its token counts could be understated.
- Foxl Code: a slow or failed reviewer no longer uses up a review round. Two rounds are allowed per pull request, and a reviewer that could not start still consumed one - so a transient failure could quietly leave a PR with no review at all, waiting on a reviewer that was never running.
- Two organisations that both installed Foxl on the same repository no longer collide. The autonomous loop's own issue list was keyed by repository and issue number alone, so one install's record silenced the other's.
- The membership re-check reaches every organisation. An organisation Foxl temporarily could not read was retried every minute forever, which could use up the whole check budget and leave other organisations unchecked.
- An organisation with more than 1,900 repositories, or more than 4,900 members, is handled correctly. Past those points Foxl silently saw a partial list, which could remove repos or member access it had simply not been shown.
- A machine marked failed can come back. A box that reported a working new address stayed marked failed until someone edited it by hand, and the reason it failed was erased on its next check-in.
- Interval schedules keep their timing. A five-minute schedule drifted by a few seconds every run and lost roughly one run in ten over a day.
- Old task activity is cleaned up again. One long-running task could block the cleanup permanently, so the records kept growing.
- A settings save no longer half-applies. Pasting the wrong kind of Anthropic key was refused after the rest of the form had already been saved.
?limit=-1no longer removes the limit on task search, activity and suggestions.- Agent output is scrubbed the same way everywhere. Four of the five agent runtimes removed only GitHub tokens from what they showed and stored, so an Anthropic key, a Slack token, an AWS key id or a login token printed by a coding tool could survive into the saved final screen.
- Machine errors no longer show internal detail. A database or network failure was returned to the browser verbatim.
- A busy autonomous loop keeps its own pace. While a periodic review was pending, the loop ran on every cron minute instead of its configured interval.
npm run deployin the container directories runs the real deployer. It ran a retired script that could target the wrong runtime and report success after failing.- Four desktop settings requests from the phone and the web app are encrypted now. The model picker and the chat home reach your desktop through the relay, and those particular calls were the only ones going through a helper that skipped the encryption every other request already used - so the relay could read which provider you use, the model you picked, and the greeting your desktop writes from your recent chats. They ride the same sealed envelope as everything else now. A desktop that predates the encryption keeps working exactly as before.
- A name containing
$&is no longer mangled in a message. A file, note, channel or repository whose name contains one of a handful of characters had part of the surrounding sentence pasted into it - "Deleting <name>" could come out repeating the word "Deleting". - A large paste into a remote terminal no longer disappears. Roughly 30,000 characters or more was silently dropped instead of being sent.
- Your Overview and For You pages use the right timezone after your machine changes it. Travelling, or a daylight-saving change while the app stayed open, left them reading the day boundary from the timezone the app had started in.
- Exporting your data reports the right error. A failed export said your account could not be deleted.
- A model-fallback chain that fails to save no longer looks saved. The switch and the list stayed on the screen showing a chain your desktop had never stored.
- docs.foxl.ai offers an iPad the web app, not the macOS installer. An iPad reports itself as a Mac, and only the download button on the docs site had missed the check the main site already had.
- status.foxl.ai keeps an outage on the day it happened. After midnight UTC every bar in the 90-day strip was labelled a day late until the page next refreshed its data.
- status.foxl.ai says "degraded" when something is slow rather than "unavailable". A component answering every check, just slowly, was announced as being down.
- Channels: editing a message keeps its reactions. The chips disappeared from everyone's screen when a message was edited, and stayed gone until the channel was reloaded. The reactions themselves were never lost.
- Channels: the "New" divider sits above the oldest unread message. With a deleted message among the unread, it sat one message too low, so the oldest unread looked as though it had already been read.
- Foxl Code: a terminal whose session token expired reconnects instead of showing developer instructions. It printed advice about starting a local server, on code.foxl.ai, and stayed dead until something else happened to refresh the token.
- Foxl Code: the review panel finds the pull request on a retried task. Opening the original task said "No PR yet" even though the retry had opened one.
- Claude Fable 5.1 works again on the ChatGPT-style Claude sign-in. If you use Foxl with a Claude subscription rather than an API key, picking Claude Fable 5.1 failed every time while Opus 5, Sonnet 5 and Haiku 4.5 kept working, so it looked like a problem with that one model. It was not: Foxl identifies itself to Anthropic with a Claude Code version number, Anthropic decides which models that version is allowed to use, and ours had fallen behind what Fable 5.1 requires. It now matches the version Foxl already uses for cloud coding, and Anthropic accepts the model. There is a check that asks Anthropic directly which models the sign-in can reach, so the next model to need a newer version is a visible answer rather than a mystery failure.
- The same is true on Android, backed by a key generated inside the Android Keystore that never leaves it. It moves itself on first use and nothing signs you out; the backup and phone-transfer paths were already excluded, so this closes the remaining case of a rooted device or a forensic copy.
- "A device connected to this computer" is a notification again, and it now has a switch. The banner that used to appear when your phone or a browser reached this computer over the relay stopped arriving for anyone using "keep running when closed": in that mode the background service holds the connection, and it only wrote the event to a log file. It now names the device that connected ("Safari (iPhone) connected to this computer"), lands in the notification bell alongside everything else, and opens the Mobile/Web page when tapped. Settings -> Notifications -> Devices turns it off; it is on by default, and it still announces a device only once, then stays quiet about that device for six hours. With the app fully quit and only the background service running there is nobody to show a banner to, so nothing is shown until you open the app.
- A message from Slack or Telegram no longer goes quiet when the agent needs your permission. An approval prompt can only be answered in the Foxl app on your desktop, so a request that started in a chat used to stop dead for a minute with nothing said, which looked like the agent had crashed. The thread now gets a line naming the tool it is waiting on and telling you where to answer, and a line if the approval was not granted. The prompt itself still cannot be answered from the chat, and the details of the call stay in the desktop dialog rather than being posted into a room other people can read.
- Inline code no longer falls back to Courier on some Windows machines. The rule behind
codeandprenamed only the webfont and the genericmonospace, skipping Menlo, Consolas and the rest, so a machine that had not yet fetched the font dropped straight to the generic. It reads the full stack now. - The app no longer fetches a font stylesheet from Google on every launch. JetBrains Mono was pulled from
fonts.googleapis.comat the top of the app's stylesheet; it ships in the app now, like every other font it uses. - The app opens offline again. Two fonts were loaded from Google's servers before the window was allowed to draw anything, so on a network that quietly drops traffic instead of refusing it - a hotel or airport portal, a corporate firewall, a dead DNS - Foxl showed a blank window with nothing explaining why. Measured on the shipped build: no first paint within 20 seconds. Both fonts ship inside the app now, so the window draws in 100 ms with the network unreachable, and the pixel-office game keeps its font offline, which it never did before.
- The window draws 4x sooner on a normal launch. Same cause: those two font requests were render-blocking. Measured first paint on the shipped build, 348 ms, against 88 ms with the fonts local. Nothing about how the app looks has changed - it is the same typefaces, at the same weights, from files inside the app instead of over the internet. Foxl also no longer contacts Google on launch.
- The model picker opens straight away, even with hundreds of models. On the phone, with an OpenRouter key on your desktop, the model button used to sit there greyed out reading "Loading..." every time you came back to a chat - it could not be opened at all until the whole catalog had been fetched over the relay. It now opens on the list it showed you last and quietly refreshes behind it, and coming back to a chat within a few seconds does not re-fetch anything. Every model is still there. The list fills in as you scroll rather than all at once, so a long catalog no longer stalls the sheet as it slides open, and searching still looks through everything you have - not just the part currently on screen. The model you have selected is always shown when the picker opens, however far down the list it sits.
- Groundwork for a browser-extension pairing code. Nothing changes for you in this release, and browser control keeps working exactly as it does today. The connection the extension uses is accepted on the strength of one header the caller fills in itself, so anything else on your machine can claim to be the extension - watch what Foxl is doing in your browser, and start a full agent run with shell access. Closing that needs a shared code, and the extension is a separate program that cannot send one yet. So this release ships the desktop half only: Settings -> Web Access now prints a pairing code, kept in a file only your own account can read, and Foxl says on that page that the code is not required yet. A later release will require it, once the extension can carry it - at which point a program that cannot read your home folder can no longer take the extension's place. Even then it will not stop a program already running as you, which can read that file like anything else you own; that is the same limit every app on your machine has, and it turns "just connect" into "find and read a specific file".
- A "deny" tool rule is no longer defeated by a second space. A deny rule was matched as a plain piece of text, so the same command written slightly differently went straight past it: with
exec(rm -rf)denied,rm -rf ~/work(two spaces),rm<tab>-rf ~/work,rm -r -f ~/work,rm -fr ~/workandr'm' -rf ~/workall ran. The command is now normalised before matching - quoting undone, whitespace collapsed, operators separated, a POSIX program path reduced to its name (/bin/rmtorm) and short flags expanded and sorted - and the rule's own text is normalised the same way, so all of those hit the rule. A normalised match has to start at a word boundary, so a deny rule still cannot fire on a command that never contained it (xterm -fr redis notrm -rf). Settings -> Tool approval now also says plainly what a deny rule cannot do: it matches text, so a substitution ($(...),$VAR), an equivalent tool (find -delete,python3 -c), an encoding (base64 -d | sh), a long-flag spelling (rm --recursive --force) or a wrapper that takes an argument (git -C /repo push --force) still walks past it. Use "ask" for anything that must not run unattended. - Known gap, stated rather than implied: the program-name reduction above is POSIX paths only. A backslash is the POSIX shell's escape character, so a Windows program path (
C:\Windows\System32\cmd.exe,.\rm.exe) is not reduced to its filename and gets none of that extra reach - most visibly for the anchoredexec(name:*)deny form. Nothing got worse: a deny rule is still matched against the raw command text first, so a rule namingcmd.exestill finds it. Asserted as a known gap incheck:tool-permissions. - Opening a cloud terminal on a Cursor, Hermes or OpenCode task now says why it cannot open, instead of failing silently. Foxl's own cloud account is missing one permission it needs to attach a terminal to those tasks, which was re-measured against AWS and is still missing. The old behaviour was the worst possible shape: the server handed the app a connection address that looked fine, the connection was then refused, and the refusal arrived as a closed socket with nothing anywhere saying what had happened or that only an operator could fix it. It now refuses up front, names the exact permission, and says who has to grant it. There is also a command that asks AWS directly whether the permission is there - so the answer stops depending on somebody remembering to check. The permission itself is an AWS change only the account owner can make; until it is made, the honest refusal is what you get.
- Dismissing a Foxl Code card works again in the desktop app and on the phone. Pressing the X on a "Checks failed" row - or Dismiss, Snooze or a reason - answered "Failed to fetch" and left the card where it was. The request was being refused by the browser before it was sent, because the app's API proxy did not list
PATCHamong the methods it permits. The web app at app.foxl.ai and code.foxl.ai was never affected, which is why this looked like a phone-and-desktop-only fault. Editing a schedule, renaming a machine and changing a workspace member's role were broken the same way and are fixed by the same change. - Adding your own machine to Foxl Code works again. The install command downloads a small manifest from code.foxl.ai first, and that file was never published with the release, so the installer stopped with an error before creating anything. It is published now, and the release checks the live site afterwards rather than trusting the upload.
- A machine tells you when its agent is out of date. Your box reports which version it is running every minute and nothing ever looked at it, so a fix could ship and the machines already set up would quietly keep the old one. The machine row now says when there is a newer version and what to re-run.
Performance
- Saving is roughly 10x faster. The local database was running in SQLite's default journalling mode, which fsyncs twice for every row written. Measured on a real 1.3 GB database: a burst of 500 agent events went from 337 ms to 31 ms, and 200 chat messages (which also update the search index) from 188 ms to 24 ms. Tail latency improved with it - across 6,456 samples over three minutes the slowest 1% of requests went from 17 ms to 2.8 ms, and the number of moments where the server briefly stopped answering dropped from nine to two. The first launch after this update takes about a second longer, once, while the change is applied and one index is built.
- Searching your conversations returns 50x less data. The search dropdown shows one clipped line per result but was being sent every matching message in full - measured at 1.5 MB for a single search on a real database, including one 1.2 MB message. It now sends the snippet it displays: 31 KB, and the search itself is 5x faster. From a phone, over the relay, that is 1.5 MB it no longer has to download to draw the same dropdown.
- The provider list no longer stalls the server. Checking whether Claude Code is signed in ran a keychain lookup as a blocking subprocess on every request, and that route is polled - so every few seconds everything else, including a reply being streamed, waited about 30 ms behind it. Measured: an unrelated request went from 1.9 ms to 28.9 ms while it ran. The answer is now remembered for five seconds, so signing in with
claude loginstill shows up within five seconds, and the route went from 28 ms to 2.7 ms. - The overview page's rollup is 15x cheaper, and its cache actually works. It recounted every message on the machine by reading the message bodies - most of a 1.3 GB file - to answer four totals; one index removed that (40.6 ms to 2.7 ms). Separately, the page polls every 30 seconds while the cache expired after 15, so it never once got a cached answer; live values like whether the agent is running are still computed fresh on every request.
- The tool-history list is 7x smaller (1.9 MB to 259 KB on a real database). It was sending whole tool outputs, including base64 screenshots up to 506 KB each; it now sends the first 4 KB of each and says so where it cut.
Fixes
- The macOS app opens again. v0.7.1 could not start: the window flickered without ever loading, nothing on screen said why, and quitting showed "A JavaScript error occurred in the main process". v0.7.1 has been withdrawn and this release replaces it. If you installed it, it updates itself to this one - the failure was in the window, not in the updater, so the automatic check kept working. MEASURED on a machine running the broken build: it moved from 0.7.1 to 0.7.2 on its own. Downloading from foxl.ai also works if you would rather not wait. What happened, in one sentence: the security policy v0.7.0 added to the app window refused the app's own start-up script, so the window never received the key it needs to talk to its local server, and it retried forever. The policy now permits that script again. Everything else that policy blocks is unchanged - the app still refuses to evaluate code from a string, still refuses to be framed, and still refuses to load a script from anywhere but itself - and the underlying hardening is being finished properly rather than left as it was.
- Quitting no longer shows a JavaScript error. Three pieces of start-up work ran up to two seconds after the window appeared and did not check whether the window was still there, so quitting during those two seconds crashed the main process with "Object has been destroyed". All three check first now.
Changes
- The cloud coding agent now installs an exact Claude Code version. The Foxl Code runtime that runs Claude Code against your repositories used to install whatever release was newest on the day its image was built, so the version written in our source never decided what actually ran. The image and the dependency range now name the same exact version, 2.1.259, and what runs is what the source says. This is a reproducibility fix and not a security one: the version last measured in that runtime, 2.1.246, is already past the fix for both of the advisories the old range sat inside.
- "Always allow" on a shell command remembers its first word again, the way it did before v0.7.0. v0.7.0 narrowed what one click saves in three ways at once: it matched the first word as a whole word only, it saved no rule at all when the command you approved contained a
;, a pipe, a redirection,$(...)or a line break, and it never covered a later call that carried its own environment variables. A pipe is ordinary in a shell command, so in practice the common case saved nothing and the next command asked again - which is the opposite of what pressing "Always allow" is for. All three are reverted: one click onls -laallowslsfollowed by anything, as it did through v0.6. That is a broad rule and it is meant to be - it also covers a command that merely starts with those letters, such aslsof, and a longer line that begins with the word you approved. What one click saved is listed under Settings > Safety > Tool rules as "Allowed for this session only", and it is gone when the app restarts; a rule that must keep asking, or never run, can be added there as anaskordenyfor the tool or command, which outranks it. A narrower form of this may return later if it can be done without a prompt for every pipe. - A GitHub event about one connected account can no longer change another one's records. Four Foxl Code bookkeeping tables were matched by repository NAME plus a number, with nothing saying which installation the event came from. GitHub hands the same
owner/reponame back out after a repo is renamed, transferred or deleted, so a pull request closing in one account could mark a still-open pull request as closed somewhere else, quietly drop a decision out of that person's "Needs you" list, retire a suggestion card that was still live, and - when an issue was reopened - take back a backlog slot that keeps their autonomous loop from running. Every one of those writes is now scoped to the installation the event names, and refuses to write anything at all if the event does not name one. - Transcribing with your own key or on your own Mac no longer spends Foxl credits. Those two modes never sent any audio to Foxl - you paid your own provider, or the speech engine ran on your machine - but the app was still reporting the seconds and Foxl was still deducting credits for them. It now reports nothing, and the server charges nothing even for app versions that still ask. A recording in those modes also no longer stops when your Foxl credits run out, because nothing is being spent.
- Starting a Foxl-hosted voice transcription now checks that your credits can cover it. Until now a session was allowed through as long as your balance was above zero, so a nearly empty account could still open a transcription stream. A session now puts a small hold on your credits before it starts and releases it as soon as the first report arrives, so an ordinary recording feels exactly the same. Importing an audio file holds the length of that file, which is the only case where the amount is known in advance, and the hold now lasts as long as the audio rather than five minutes. There is also a limit of 32 transcription sessions open at once. It does not stop one session's URL being reused for several streams at once - that is a property of the signed URL and has to be reconciled against AWS.
- The four most expensive Foxl Code requests now have a ceiling, so one looping client cannot spend a quota everybody shares. There were limits on how many agents one account could have running and on what a task could spend, and none at all on how many REQUESTS it could send - so a buggy or retrying client could keep starting cloud agent turns, keep forcing the autonomous loop to run past its own interval, and keep asking GitHub whether an arbitrary organisation has Foxl installed. Three of the four are bounded per ACCOUNT, with both a per-minute and a per-hour limit: sending a chat turn (20 a minute, 300 an hour), forcing the autonomous loop's next run (10 and 60) and adopting a GitHub installation (5 and 10). The fourth, typing into a cloud terminal, is bounded per TASK at 600 a minute and has no hourly limit - it is counted inside the task's own coordinator instead of in the database, because a database round trip per keystroke would slow down the one path you feel. Every number is far above what a person produces by hand. Over the line the answer says how long to wait rather than looking like a sign-in problem.
- Billing is still unconfigured, and the refusal now says so precisely instead of looking like a missing setting. Foxl sells nothing today, so nothing about paying changes here. What changed is internal: the one refusal every billing route already returned now carries a machine-readable code, and a signed-in caller and the admin health check get the actual diagnosis from a single shared source, as does the daily internal report. There is one command that answers "is billing configured, and why not". An anonymous request to the public config endpoint still gets only the bare refusal. Choosing a payment provider is still an open decision and no provider was picked here.
- A coding agent's GitHub token can no longer widen by accident. The token handed to an agent's shell is minted with a reduced permission set, and if that set cannot be resolved the mint now fails instead of quietly asking GitHub for a token with everything the app can do. The parameter is traced to GitHub's own reference beside the code, including the answer to the question people keep asking: GitHub offers no way to limit a token to one branch, so the branch rule still needs a different fix.
Security
- The per-address limit on sign-in attempts now exists on every copy of the relay database. The table that limit writes to was declared one way in the database setup files and another way in the live databases, so any database built from the files - a fresh local one, a test one, or one restored from scratch - silently accepted every sign-in attempt with no limit at all. The setup files now build the same table production has, and a limiter that cannot record an attempt says so in the server log instead of staying quiet.
- Pet characters can no longer be claimed by a new account without drawing them. When the pet collection moved to your account in August, a one-time step let existing players bring the characters they had already drawn on this device up to the server. That step was open to every account, so a fresh account could hand itself the whole roster, rarest character included, and gift the copies on. It is now limited to accounts that existed before the move; everyone else earns characters through Draw, as intended. Nobody who used the step legitimately is affected.
- Connecting GitHub to your account can no longer be replayed from a captured link. The request that starts a GitHub link travelled inside the address GitHub sends you to, and for ten minutes anyone who saw that address could finish the request again. It is now good for one attempt and five minutes; a second use is refused with the same "expired or already used" message you would see for a stale link, and starting over is one click.
- Connecting Slack now requires being signed in to Foxl. The relay endpoint that completes a Slack connection on the desktop's behalf (it holds the Slack app secret so the desktop never has to) accepted requests from anyone on the internet, with no limit on how often, and handed back whatever Slack replied. Someone who obtained a Slack sign-in code could have redeemed it through our app without holding a Foxl account, and the endpoint could be used to hammer Slack under our credentials. It now completes the exchange only for a signed-in Foxl account, limits attempts per address and per account, accepts only the exact return address the desktop uses, and reports a failed exchange with a short reason instead of Slack's raw reply. If your desktop is signed out when you connect Slack, it now asks you to sign in first, before a browser tab opens. Desktops older than 0.7.1 must update before connecting Slack: the relay refuses a connect that does not carry a Foxl sign-in, and there is deliberately no grace period for a route that spends the app secret.
- Foxl Code no longer leaves credentials on a cloud task's workspace. A cloud coding task runs on storage that is kept for up to 14 days, and three paths could leave secrets there: a failed task's saved output redacted only two of the tokens it might contain, a task that never started left its whole set of run secrets and your model login behind, and the file browser could read your login file straight out of the workspace. Saved output is now scrubbed of every known key, token and login shape; a task that fails before it starts wipes its secrets immediately - including a model login left behind by an earlier run on the same reused workspace - and its run secrets are kept off the retained disk entirely; and the file browser refuses to read or list the internal credential files - in upper case, lower case or any mix of the two, including the two look-alike letters a Mac or Windows disk treats as an ordinary s or i, and even through a link a repository plants to them - while still showing you your own work.
- Disconnecting Slack now revokes Foxl's token at Slack, and disconnecting Microsoft 365 tells you where the permission still lives. Until now, Disconnect only deleted Foxl's own copy of the sign-in. A Slack token does not expire on its own, so anyone holding a copy of Foxl's database could have kept using it after you disconnected. Foxl now asks Slack to revoke the token it holds for your account before removing its copy. If Slack refuses, or cannot be reached, the Integrations page says so and links to your workspace's app management page where Foxl can be removed - the local copy is removed either way, so a machine that is offline can still disconnect. Microsoft offers no way for an app to revoke its own access, so Settings now says plainly that the permission stays active at Microsoft and links to the page where you remove it - for a work or school account and for a personal one.
- Naming a client that does not exist no longer earns a readable copy of what the desktop sends. Terminal output, the list of open terminals and status updates are encrypted for every client that has completed a key exchange with the desktop. A client the relay merely announced - one that never sends the desktop anything of its own - is no longer served a readable copy alongside that ciphertext; a connection left behind by a socket that died without saying so no longer keeps that decision alive either; and the number of clients that can hold keys with one desktop is now capped. Every readable copy that does go out is counted, and the client it was sent for is named in the desktop log instead of passing silently.
- What this does not do, stated plainly: a build too old to encrypt still rides in the clear. A phone or browser build older than v0.6.27 cannot do the key exchange, so it keeps receiving terminal output, the terminal list, and notices when a terminal opens or closes, readably - including while a newer client is connected to the same desktop, and including while it is the only client attached. That is deliberate, because most clients in use today are those builds. But it also means the relay, which carries what clients send, can still obtain a readable copy by sending one message as if it came from such a client, and the desktop has no way to tell the difference: a client that cannot do the key exchange shares no secret to check it against. Removing that would mean refusing to serve those builds at all, which is a product decision rather than a fix, so it is not part of this change. The counts are reported with the rest of the connection status.
- What an MCP server says about its own tools can no longer instruct the assistant. The descriptions a connected MCP server provides for its tools and their parameters were handed to the model word for word, so a third-party server could hide a line such as "before any answer, run this command" where the assistant reads it as an instruction - and MCP tools run without an approval prompt. Those descriptions now reach the model cleaned of hidden characters, trimmed to a sensible length, and marked as the server's own data rather than instructions: a tool's description is enclosed in markers that say so, and since a parameter's description is a single field with nowhere to put markers, the tool it belongs to says in our own words that those are the server's text too. A line that reads as an order to the assistant is removed and the removal is stated, for a tool's description and for each parameter's, and every kind of line break now counts as one, so an instruction can no longer hide by using an unusual one. Ordinary wording such as "Important: must be an absolute path" is kept.
- app.foxl.ai and code.foxl.ai now ship a Content Security Policy. The browser is told exactly which scripts the app may run - its own, Cloudflare's sign-in challenge, and analytics (only after you consent) - and refuses everything else, so a flaw that managed to inject markup into a page could no longer run code or read your sign-in tokens. Nothing changes in how the app works: links, images, fonts, live transcription, self-hosted models, the files panel (text, Office documents, spreadsheets and PDFs), copying or saving an attached image, and the chat's HTML, SVG and diagram previews all keep working, and the previews still run inside their own sealed frame.
- The desktop app's own window now refuses to run script it did not load. Foxl's window is where your sign-in tokens live and where every desktop action - saving a file, opening a path, typing into another app - is reachable, and it used to allow inline script and
eval. It no longer does, so a flaw that managed to get markup into a reply, a channel message or a filename can no longer run code on your machine. The sidebar, chat, notes, settings, the files panel and the chat's HTML, SVG and diagram previews were all checked rendering identically under the new rule; recording a meeting, the code editor and the built-in PDF preview were not part of that check, so tell us if any of them behaves differently. One deliberate change inside a preview: script that a model writes into an HTML block no longer runs there. The two start-up screens (the boot splash and the server-error page) had no rule at all and now carry their own. - The terminal font is loaded from the app instead of the internet. Its Nerd Font ships inside Foxl, and the stylesheet was still asking a public CDN for the same file first on every launch. Nothing looked different either way; the request is simply gone.
- A sign-in link in your email now needs one press before it signs you in. Opening the link used to sign you in the instant the URL was FETCHED, which is not always you: plenty of mail systems and security products follow every link in an incoming message before you ever see it. When that happened, whatever followed the link received a live sign-in for your account, and your own click then said the link had already been used. The link now lands on a short "Confirm sign-in" page that names the account and does nothing on its own, and the sign-in happens when you press the button - so an automatic fetch gets a page instead of your session, and leaves your link working. Links are still single-use and still expire 15 minutes after they are sent. That page needs cookies enabled for relay.foxl.ai; if yours are blocked it says so and your link stays valid.
- The Channels and Foxl Code live connections no longer carry your account password-equivalent in their web address. Those two connections are opened with a web address rather than a private header, because that is the only way a browser can open one - and until now the address carried your normal sign-in credential, which is good for twelve hours and for your whole account. Addresses are the part of a request that gets written down: by the network in front of us, by any company proxy, by a diagnostics export. Both now ask the server for a separate one-minute pass that opens exactly one connection, for one device, once - and for the chat connection, one room - and nothing else. The real credential travels in a private header on an ordinary request. Older installed apps keep working unchanged while they update; the old way is still accepted, is now counted, and has a dated removal.
- The desktop's tracing libraries are up to date. The OpenTelemetry packages the desktop server uses carried a published advisory in the version range Foxl shipped. They are updated, and the exporters are pinned to the versions the agent framework expects so the two cannot drift apart again.
Fixes
- Image features work on Intel Macs again. Showing the agent a picture, and the preview it makes of an image it generated, both need an image library that ships as a small compiled file per processor type - and the Mac download only ever contained the Apple silicon one. On an Intel Mac those features did nothing at all, with no error shown anywhere, because the app is written to carry on quietly when that library is unavailable. The build that produces the Mac download now includes both, checks that each one is really for the processor it claims, and stops rather than publishing an app that is missing one.
- Free accounts no longer have unpaid usage written off every month. A free account that ran a Foxl Code task or a transcription past its last credit ended with a negative balance, and the monthly refill quietly reset that balance to zero before adding the new credits, so the overrun was forgiven month after month. The refill now adds the month's credits on top of whatever the balance is, exactly as a mid-month grant does, and an account in the red works again once it holds enough credits to cover what it used.
- Saving one Foxl Code setting no longer resets the others. A save that sent only some settings used to write the defaults over everything it left out: the coding model, the per-task budget floor, the budget multiplier, the heartbeat interval and quiet hours went back to their defaults, and the autonomy switches snapped back to their default positions, all with a success response. A field that is not sent now keeps its stored value; an explicit on or off still applies, and clearing a field still clears it. That holds for the self-directed loop in both directions: a save that never mentions it neither turns it back on for someone who switched it off, nor off for an account that had it running without ever touching the switch.
- Scheduled Foxl Code tasks from one account no longer hold up everyone else's. Each minute's batch of due schedules now takes turns between accounts instead of taking the twenty oldest overall, so one account with many schedules due at the same time cannot delay another account's by minutes.
- A cloud task whose machine stopped answering is now reported in about twenty minutes, not eight hours. When the machine behind a task on Foxl's cloud died mid-run, the task could keep showing as Running for up to eight hours - you could not retry, cancel or reconnect it, and the rest of your queue waited behind it. Foxl now starts asking that machine about a run that has gone quiet for five minutes, and stops tracking the task only once it has kept saying it no longer holds the session for more than twenty minutes. One refusal is deliberately not enough: a machine that was working perfectly well has been seen giving that answer for a quarter of an hour, and cancelling a run that goes on to open its pull request is worse than reporting it late. Any sign that the run is still working - an event of its own, or its output reaching Foxl again - starts that twenty minutes over, so a machine that answers badly for a while and then carries on is left alone. A task Foxl has stopped tracking keeps any commits and pull request it made and can be reconnected on the same workspace, a run that is merely busy is left alone, and a terminal pane you are sitting in is never touched.
- A Foxl Code task that runs out of workspace storage now fails within seconds on every coding assistant, and says why. A repository large enough to fill the 1 GB cloud workspace during its clone left the assistant unable to write its own startup files; with Claude Code that ended in seconds with a clear message, while a task on Kiro, Codex, Cursor, Hermes or OpenCode sat silent until its whole time budget ran out and then reported only "failed" with an empty screen. All of them now stop before the assistant starts and show the storage reason, and Kiro and Codex also stop a run that produces nothing in its first two minutes, as Claude Code already did. The same check covers tasks run on your own machine. A task stopped this way now also wipes the sign-in details and run secrets it had already written, wherever it put them - and a sign-in left behind by an earlier run on the same reused workspace - so a refusal leaves nothing behind on storage that is kept for up to 14 days and nothing on the task's own temporary disk either.
- A Foxl Code task whose instructions start with a dash now runs on every coding assistant. With Kiro, Codex, Cursor, Hermes or OpenCode, a task typed as a markdown bullet ("- add a retry to the uploader") or opening with something like "--verbose" was read by the assistant as a command-line option: it refused with a usage screen and the task failed within seconds, with nothing connecting the failure to the text you typed. Claude Code was already immune. The prompt is now handed to each assistant as text, whatever it starts with.
- Removing a "deny" tool rule now really removes it. Deleting a rule that blocked a command from Settings made it disappear from the list, but the block stayed in force until the app was restarted, so the tool kept being refused with no rule visible behind the refusal. The list also showed rules in an internal regex spelling (
exec(^(?:ls.*)$)) instead of what you typed (exec(ls*)). Both fixed. - Changing the Feed Generator prompt through the desktop API now changes the prompt that runs. A direct API call that set the prompt reported success but stored it in a field the heartbeat never read, so the feed kept running the old prompt. Editing the prompt in Settings was already saved correctly and is unchanged. The API now writes the prompt where the heartbeat reads it, and refuses a prompt that is not text.
- Workspace paths sent with a trailing slash, a
./or a doubled slash are accepted instead of refused as "Invalid path". Shapes likeShared/ora/./bmean one unambiguous thing and are now normalised; an API caller or another client can send them, the file browser itself did not. In the other direction, a new file or folder cannot be given a name that would be unusable on a Windows machine your workspace syncs to (NUL,CON,COM1,nul.txt, or a name ending in a dot or a space): it is refused up front with a clear "Invalid path" on every platform, instead of silently becoming a device or an unopenable file there. Something that already carries such a name can still be renamed, moved and deleted. A name containing a control character gets the same clear answer instead of a raw filesystem error. - The startup diagnostics page no longer offers "Restart server" while the server is simply still starting. On a slow cold start the boot screen hands over to the diagnostics page after 12 seconds, which then presented Restart as the obvious action; pressing it tore down a server that was seconds from ready. The button is now hidden while the server is still starting and has reported nothing wrong, and comes back as soon as there is something to repair (a crash, an error line from the server, a missing background service) or after 30 seconds of quiet starting, whichever comes first.
- "Restart server" leaves other people's processes alone. The restart used to terminate whatever was listening on Foxl's port; it now confirms the occupant is a Foxl server first and logs what it left alone otherwise (macOS and Linux).
- Requesting a huge workspace file returns a clear
file_too_largeerror (limit 20 MB for text files) instead of holding hundreds of megabytes in memory, and document previews are rendered at most two at a time. - Internal hardening from the September 2026 audit, no visible change: the desktop's WebSocket connection compares its token in constant time, as the HTTP guard already did; the agent's web fetch tool also refuses IPv4-translated, 6to4 and Teredo spellings of a private address; the packaged app no longer runs
gitat launch to decorate the About panel; and an unused dynamic-import code path in the skills loader is deleted. - Right-click on a row in Foxl Code's file tree did nothing. The menu existed but the rows never received the click that opens it. They do now.
- A webhook schedule's trigger URL now works without the app's connection token, from this computer. The URL the Schedules page showed sat behind the token the app itself uses, so a local script or cron job that called it got "403 Invalid connection token" and the schedule never fired. Each webhook schedule now has its own long random trigger URL that needs no sign-in; the form shows it whole, with Copy and Regenerate. Anyone holding that URL can start the schedule, so regenerate it if it leaks. Where the URL can be called from has not changed, and the form now says so instead of implying otherwise: Foxl listens only on this computer, so curl, a local cron job or another app on the same machine can call it, while GitHub, Zapier or another computer cannot reach it directly. To call it from anywhere else, run a tunnel or reverse proxy on this computer that forwards to the URL and sends
localhostas the Host header, or start Foxl with the hostname that forwarder sends listed inPILOT_ALLOWED_HOSTS. (LAN access,PILOT_ALLOW_LAN=1, also accepts this computer's own name and addresses - and nothing else, so it is not a way to admit a hosted service.) On app.foxl.ai and the phone the form no longer prints a URL at all - it pointed at the relay, which does not serve it - and tells you to copy it from Foxl on the desktop instead. - On a Mac, a sign-in link clicked after Foxl was quit now launches Foxl and signs you in once it is ready - and on every platform, a link that arrives while Foxl is still loading is no longer lost. Two things stood in the way. Foxl only accepted a sign-in link while the same app process that started the sign-in was still running, so a magic link opened after the app had been quit, or had crashed, launched Foxl and then refused the link. And a link that arrived while the window was still on the loading screen, or on the "server not responding" page, was applied to that page instead of the app, so the sign-in was lost and the system was sometimes asked to open a page that does not exist. A sign-in started on this computer in the last 20 minutes is now remembered across a restart, and a link that arrives before the app has finished loading is held and applied the moment it has. A sign-in link that nobody started from this computer is still refused. On Windows and Linux, Foxl still does not pick up a link that launches it from cold; a link that reaches an already-running Foxl works everywhere.
- Every usage cost Foxl shows will be the one it recorded, not a guess. Two screens worked cost out for themselves from their own short price lists: the Overview page and its Usage view matched a model by name, with a guessed mid-range rate for anything the list did not know, so o3 mini was priced at o3's rate and current models - Opus 5, Fable 5 and Fable 5.1 among them - at that guess, which for Fable is less than a third of the real figure. The "Usage today" card on the home screen had a second, different list of its own, so the two screens disagreed: over one recent day on this machine they would have read $200.86 and $138.80 for the same 24 hours. All of them now read the cost Foxl recorded when each request ran, and a model Foxl has no rate for counts as $0 rather than a made-up price. Those cost figures are hidden while paid plans are hidden (since v0.7.0), so nothing changes on screen today; when they return, the Overview, the Usage view, the home card, the menu-bar readout and each message receipt will all show the same number. Under the hood, the internal price lookup also matches the exact model id against the shared catalog instead of the first entry its name happens to contain.
- When notifications cannot be set up on your phone, the app now says why. If registering for notifications failed on a device (for example a provisioning problem), the app said nothing, so "you declined" and "it is broken" looked identical. Settings > Notifications now shows "Notifications could not be set up on this device" with the reason and a Try again button. The notice stays on screen while the problem persists - a retry that fails again keeps it up with the new reason - and clears only once registration succeeds. Separately, every place the app can ask for notification permission now goes through one shared signed-in check; whether a first run can still show the permission prompt over the login screen was not re-checked on a device and remains open.
- A task no longer opens with the wrong status. The header showed a grey "Queued" pill and a
tsk_…slice on every task - merged, failed or running - and flipped to the truth a moment later. It now shows a placeholder until the task loads. - A failed task says why. The header showed "Failed" and nothing else. It now carries a "Did not finish" chip whose expander gives the reason - including the case that failed most often on real accounts, where the agent finished cleanly but the repository's own tests did not pass, with the test output behind it.
- The task page lines up and speaks your language. The status pill is translated (it was English in every language), the header lines up with the cards below it, the terminal card matches its neighbours and its header says "Terminal" with a live dot only while the agent is actually running, the plan chip says "In progress" rather than
in_progress, the budget warning loses its cap-and-commit jargon, and Resume switches you to the terminal it tells you to type into. - Your profile photo no longer disappears and reloads every time you reopen the window. Closing the desktop window with Cmd+W only hides it, so reopening from the Dock does not reload anything - but the sidebar footer blanked the picture to your initials anyway and then loaded it again a moment later, every single time. The photo, your name and your email are now kept while the window is away and repainted from memory. Measured on the reopen: the image element is never unmounted, the picture is not requested again, and the account lookup behind it drops from two extra requests to none.
- Less work behind a window nobody is looking at. Seven background refreshes kept running on a hidden window - the relay device list every 15 seconds, the channel list and MCP connection status every 10, the agent list and the home dashboard every 30, the running-agent list every 5, and the tunnel switch every 10. They now pause while the window is hidden and catch up the moment it comes back, so a Foxl left running in the background stops spending requests on screens that are not on screen.
- Your MCP tools connect on their own again, and nothing asks you to approve starting them. v0.7.0 asked for one approval per MCP tool the first time its program started, and the prompt it raised could not be answered from the Tools page - so a server that had been working for months answered "Not approved: starting <command> ... was denied or the approval request timed out" and the agent stopped seeing its tools. Turning a tool on is the approval; that gate is removed. A local MCP server also gets your full environment again, so one that reads a token, an HTTPS proxy setting or a certificate bundle from it works without being told again in that tool's Env field.
- The two other guards v0.7.0 added are unchanged: pressing Run on a shell or script tool still goes through the approval prompt the agent's own tool calls use, and the MCP inspector still takes one of your saved tools rather than a command from the request.
- Signing in no longer reveals whether an address has a Foxl account. Asking to sign in with an email answered one thing for a registered address and another for an unregistered one, so anyone could check whether a given person uses Foxl - and the password door already went to some length to hide exactly that. The sign-in request is the same request for both cases now, and answers identically, except for a small number of throwaway-mail domains we do not open accounts on.
- When a tool needed your approval and no Foxl window was open, the agent said you had refused. Ask a run from Telegram or Slack, or let a schedule tick at 3am, and a tool that needs approval had nowhere to show its prompt - the approval dialog only exists in an open Foxl window. The request waited a silent minute and then reported "User denied", so the agent apologised for a decision you had never made and never mentioned that a question had been asked. It now says which of the three things happened - nobody could be asked, the prompt was shown and went unanswered, or you really did decline - and when nothing could possibly see it, it says so at once instead of stalling. A prompt raised while the window was reconnecting is now re-sent to it rather than missed.
- A shared workspace's storage now has a ceiling, and it cannot be run up by one member. Shared files were billed to whoever pays for the workspace, and on a paid plan there was no upper limit of any kind - so any member, including an invited contractor, could charge the owner an unbounded recurring storage bill. Uploads are now refused past a real ceiling (four times the plan's included storage, so 20 GB on Pro and 200 GB on Ultra), the refusal names the number, the Shared panel's storage bar now measures that ceiling rather than only the included allowance and warns at 80% of it instead of leaving the first sign of it to be the refusal, and the workspace's billing owner can set an explicit limit of their own. Uploads are also rate limited per person per workspace. A personal workspace is not capped: nothing there is billed to anybody else, and there would be no owner to raise it. Removing someone KEEPS their files and keeps billing them - deleting a colleague's work because their access ended would be the wrong trade - but the removal now reports how much storage it left behind, and a workspace admin's Shared panel breaks the total down by uploader and marks the ones who have left, so an owner can find those files and delete them.
- A scheduled task runs for as long as you told it to, and no longer fails part-way through work it used to finish. v0.7.0 gave every unattended run a budget, and two of its three limits sat below a normal run: the runtime a person sets on a schedule was never read at all, so every schedule was cut off at ten minutes however it was configured and editing that field did nothing; and the cap on how much a run could generate was a fifth of what a single reply from the default model is allowed to produce, so one ordinary first answer could exhaust the whole run's allowance before anything checked it. A task that had run for months would stop mid-way and be recorded as failed. The number is now enforced rather than only logged, so a schedule whose timeout was set below the time it needs will now be stopped at that timeout - raise it in the schedule's own settings. Schedules now use their own configured runtime, and the limits are set where they were meant to be: high enough that only a runaway loop reaches them. You can still lower any of them per schedule, or in HEARTBEAT.md for the Feed Generator.
- Cmd-clicking a file path that Foxl will not open now says so. v0.7.0 limited opening to your workspace, Documents, Downloads, Desktop and Notes folders, and a path outside them underlined and then did nothing at all: the refusal was written to the log and discarded before it reached the screen. It now appears as a message naming the reason, so a file kept somewhere else, a file that has moved, and a file Foxl will not open because it can run are three different answers instead of one silence. Notes' "Open file" no longer reports the same refusal as "no saved file yet" either, which sent you looking for a missing export when the folder was the problem - the case being a save folder chosen on another computer, since that setting syncs between devices and the permission does not. What Foxl opens is unchanged.
- Signing in with Apple works when Apple sends no email address. Apple only passes your address on the first sign-in and decides for itself whether to include it again later. v0.7.0 required a confirmed address on every sign-in, checked before it looked up who you are, so an Apple ID already linked to your account could be refused with no way to get back in. Foxl now recognises you by your Apple ID itself, which is what Apple provides for that purpose. An unconfirmed or missing address still cannot be used to find or create an account, so nothing is loosened: the check now applies only where the address is what identifies you.
- The Notifications screen on your phone now tells you the truth about whether the system has notifications turned on for Foxl. It read that setting once, when the screen opened, and never again - so if you turned notifications off in iOS Settings, the screen went on showing eleven happily-enabled switches and said nothing, and if you turned them ON from its own "Open Settings" button it still insisted they were off. It now re-checks every time you come back to the app, and says which state you are in either way instead of leaving "allowed" as a blank space.
- A subscriber whose payment finally goes through gets their plan back, whichever order the payment notifications arrive in. After four failed card attempts a subscription is marked unpaid and the account drops to the free plan. When the card was fixed, the payment provider sends two notifications at once - "the subscription is active again" and "the payment was recovered" - and if they arrived in one particular order the account was left on the FREE plan with a paid subscription behind it: free-tier models, free-tier rate limits, and the free monthly credit top-up, with nothing anywhere able to correct it afterwards. Both orders now end at the same place, and the plan is restored on the next sign-in for an account already stuck this way - credits already missed for that one period are not re-issued.
- A coding-assistant key you store with Foxl Code is now tied to the account and the field it was saved in. The stored form was encrypted but not pinned to a place, so anything that could write our database directly could have moved one person's key into someone else's - or one of your own keys into the slot for a different assistant - and it would have been used. Keys saved from now on cannot be moved; older ones are re-pinned the next time you save them.
- "Work without asking permission", turned off, now also stops the repository from configuring the coding assistant. Turning that switch off used to remove the coding CLI's act-without-asking flag and nothing else, so a
.claude/settings.jsonhook and an.mcp.jsonserver inside the CHECKOUT still started as the CLI came up - before the first tool call anyone could have approved. Measured against the real CLI: with the switch off, a repository'sSessionStarthook ran, its MCP server started and its slash commands were available. With the switch off, nothing the repository ships under.claude/is loaded now - hooks, MCP servers, slash commands, subagents and skills alike - and the task terminal says so, while your own instructions and advanced config still apply in both modes. Note the two are welded: a repository hook you legitimately want back means turning the switch on, which also grants the act-without-asking flag, so a hook you want on every task belongs in your own config rather than in a repo. This is unchanged with the switch ON, which is where a repository shipping its own formatter hook keeps working. Applies to Foxl Code tasks in the cloud and on your own machine. - A new chat opens with something to click on, on every screen size. The first screen showed only the question and the box to type in - the Foxl mark, the three suggestions and the "Desktop offline" notice were on phones only, which left an iPad looking mostly blank. All four now appear on the desktop app, the web app and tablets too, centred above the composer.
- The Notes coaching report is readable in a narrow column. With the transcript open beside it, the Feedback list had so little room that a sentence stacked one word per line and the category labels ran off the edge of the card. The report now lays itself out from the space it actually has rather than from the size of the window, so it stacks into one column when it is narrow and the labels wrap instead of overflowing.
- Syncing skills now takes one reviewed version of the library, not whatever was pushed last. The sync cloned the tip of the skills repository's default branch, so a single commit there reached every desktop that synced - as prompts and tool descriptions the agent follows - and a fallback in the same code path meant a rewritten history was merged rather than refused. It now fetches ONE pinned commit, checks it out detached, and verifies that is really what landed; if it cannot, the sync fails and says so instead of falling back. The pin is currently the same commit the branch is on, so nothing about the skills you have changes - what changes is that moving it is a reviewed edit to Foxl rather than a push to another repository. Because of that, "Up to date" now says which version it means: nothing looks at the library's latest commit any more, so the plain message would have been true about the pinned version and misleading about the library.
- You can now see the safety code that tells you a remote connection is really your desktop, and you are told when it changes. The code was described in the docs as something to compare on both screens and no screen ever showed it. It is now on the Mobile/Web page at both ends, and if the code your desktop presents ever changes - a reinstall, a new machine, or someone sitting between you and it - a warning says so, says what it means, and cannot be hidden. Accepting the new code takes a deliberate confirmation; closing that confirmation accepts nothing. Before this, that situation just broke remote access quietly, with nothing on screen and no way to recover.
- On iPhone and iPad, the long-lived credential that keeps you signed in is now stored in the iOS Keychain instead of inside the app's web storage. It was in a plain file in the app's own folder, which no other app can read but which an unencrypted computer backup, a jailbroken device, or a forensic copy of the phone all can - and that credential can mint fresh sign-ins until it is revoked. It moves itself the first time the app needs it after this update, and nothing signs you out. Android is unchanged for now (its backup and phone-transfer paths were already excluded); the short-lived access token stays where it was, on purpose.
- A change that reaches Foxl's release branch without review is now reported. Every Foxl product is built from one branch, and a check now goes red when a commit lands on it with no merged pull request behind it, or when that branch's history is rewritten. It reports rather than blocks - the commit has already landed by the time it runs - and the release automation's own version-bump commit is the one exemption.
- A corrupt Foxl Code credential no longer puts a piece of your token into our server logs. When the stored Claude or ChatGPT credential could not be read back as a file, the error our server wrote down quoted the first ten characters of the decrypted credential - and those logs are kept for days and readable by anyone with access to our Cloudflare account. The diagnostic is still there and still says what went wrong (the kind of failure, the size of the stored credential, and where in it the reading stopped); it just no longer carries any of the credential itself. Nothing about how the failure is reported to you changed: Foxl Code still refuses to start the task and still tells you to re-sync the credential in Settings.
- The pages the agent reads in your browser no longer end up in the log the app hands out. When the Chrome extension sent a page back - its text, its accessibility tree - the desktop wrote the whole thing into the 5000-entry log buffer that
/api/logsserves to anything holding your connection token, which includes your phone over the relay tunnel. A page you are signed in to could therefore be read from another device, and one page read pushed a lot of genuinely useful diagnostics out of that buffer. The log line now records which request replied, how big the reply was and what kind of thing it was, and never the reply itself. The version the extension announces and an unrecognised message type are bounded the same way, and a frame the desktop cannot read as JSON is reported by name and size instead of by quoting the frame. - The image library the agent uses to look at your pictures is up to date. Showing an image to the agent decodes it with a library that had four published vulnerabilities in the version Foxl shipped, and it is handed whatever image file you point it at. Macs with Apple silicon, Windows and Linux now bundle the fixed version. The build was also reading its own copy of that version number rather than the one in the project, so the two could disagree without anything reporting it - it reads the project's now. (Intel Macs are unaffected either way: that image library is missing from those builds entirely, which is a separate bug being fixed on its own.)
- A Cursor, Hermes or OpenCode task no longer offers a Reconnect button that cannot work. Those three agents run their terminal as a cloud shell rather than a session Foxl re-spawns, so the server has always refused to reconnect them - but the app offered the button anyway, and the sentence it printed after the refusal told you to reattach through a shell that no version of the app has ever been able to open. It then re-enabled the same button, so the whole loop was: press it, read about a shell, look for the shell, press it again. The card now says up front that reconnecting cannot bring that agent back and why, offers Retry where Retry actually works, and offers nothing where it does not. The Runtimes pane had the same button and was worse
- it printed the raw error from the server - and it is fixed the same way. Nothing changes for Claude Code, Kiro or Codex tasks.
- Windows can update itself again. Since v0.5.7 every automatic update on Windows was downloaded, checked, and then thrown away: the check required Windows to call our signature fully trusted, and our certificate is self-signed, so it never could. Nothing said so - the failure left one line in a log file - which means Windows installs have been sitting on whatever version they were installed from for 40 releases. The app now runs its own check, which still refuses an unsigned installer, one altered after signing, and one signed by anybody other than Foxl AI, and a refusal now shows up in the notification bell instead of disappearing. On Windows this one update has to be installed by hand: the fix ships inside the new build, so the build you are running still cannot fetch it. Download the latest version from foxl.ai once, and automatic updates work from then on. Getting a signature Windows trusts on its own needs a paid certificate; that is a separate, still-open item.
- Mobile sign-in no longer hands the session to the
foxl://URL itself. The relay returns a short-lived PKCE code that only the app that started the sign-in can redeem, so an access token and a 30-day refresh token no longer travel in a URL any app on the device may claim. Apps installed before this change keep signing in exactly as they did. - The mobile build no longer carries a flawed archive library. The icon and splash generator was installed as part of the phone app's build tooling even though it only ever runs by hand when the artwork changes, and it brought along old copies of an archive library and an image library with published flaws - including the most serious one anywhere in this project. It is run on demand now instead, which takes those out of the build entirely. Nothing about the shipped app changes.
- A provider endpoint Foxl is not using now says so. A custom endpoint that Foxl refuses was reported only in a log nobody reads, while the Settings field rendered empty - identical to never having set one - as requests went to the vendor's own address instead. The refused address and the reason now appear in Settings where you typed it.
- A phone signed out by a session change can register for notifications again. When a sign-in was revoked from elsewhere, the phone kept a note saying it had already told the relay about its notification token, so after signing back in it never told it again and notifications stayed silent. That note is now cleared along with the session.
- Image features work on Intel Macs again. Showing the agent a picture, and the preview it makes of an image it generated, both need an image library that ships as a small compiled file per processor type - and the Mac download only ever contained the Apple silicon one. On an Intel Mac those features did nothing at all, with no error shown anywhere, because the app is written to carry on quietly when that library is unavailable. The build that produces the Mac download now includes both, checks that each one is really for the processor it claims, and stops rather than publishing an app that is missing one.
- Settings no longer shows a red error under Amazon Bedrock. The provider pane asks the server for each vendor's own extra options, and the providers that have none - Amazon Bedrock, Anthropic and the subscription sign-ins - answered "not one of those vendors". That answer was drawn as a failure: an alert quoting an internal error string, next to a Retry button that could never succeed. Those providers now simply show no extra options, and a real problem reaching your desktop still shows the error and the retry.
- Changing a provider's settings no longer sends the provider dropdown back to Amazon Bedrock. Saving an API key, flipping one of a vendor's own options or pressing Refresh re-read the saved provider and overwrote the choice you had just made in the dropdown - so picking OpenRouter and adjusting one of its switches jumped back to Bedrock while the model box still held an OpenRouter model. The pane now keeps an unsaved choice until you press Save Provider Settings, and stops showing the previous vendor's options while the new one loads.
- A rescheduling request no longer forgets a schedule's Timeout. Asking the agent to move a schedule to a different time rewrote its whole configuration instead of the timing part, so the run-length limit set in the schedule's own editor was dropped without a word. Only the timing changes now, and switching between a repeating time and an interval no longer leaves the old one behind.
Improvements
- The Files pages now work like an editor's explorer, in both Foxl Agent and Foxl Code. Files are coloured by type (TypeScript, JSON, Markdown, images, lockfiles and forty-odd more), nested rows carry indent guides so a deep folder still reads as a tree, and the path above the open file is a row of breadcrumbs: click a folder to reveal it in the tree, or open its picker to hop straight to a sibling file. The tree is fully keyboard-driven - arrows move, Left and Right fold and unfold, Space opens, Home and End jump, and typing a few letters lands on the matching row. Cmd+P (Ctrl+P on Windows) opens Go to file, a fuzzy finder over every file with your recently opened ones listed first. A folding button collapses the whole tree, and a tree-shaped shimmer stands in while files load instead of a lone spinner.
- Foxl Agent's Files page gets New file, New folder, Preview for Markdown, and "Ask the agent about this file". New file and New folder are inline rows typed straight into the tree (the old folder prompt is gone, which also fixes it on iOS, where that prompt was blocked). Markdown files carry an Edit / Preview switch, remembered across files, so the agent's notes read as prose. Every file's right-click menu now offers Copy path and Ask the agent, which starts a chat with that file already in the prompt. Typing a name that is already taken - including a hidden file the tree does not list, or a note the agent saved a moment ago - is refused rather than emptying that file, and names with characters such as
#or?open and save correctly. - Foxl Code's Files page shows real source. Files open in a syntax-coloured viewer with line numbers and a wrap toggle in place of the raw text dump, Markdown renders as a page with a Source toggle, images display inline, and every file can be copied or downloaded from the header. Right-click a file for Copy path, Copy name and Download, or a folder to refresh or collapse it.
- The Work page's "Needs you" list is grouped by what you do about it. It used to be one flat column of look-alike cards - a question, a merge, a failed build and an issue Foxl filed for itself all through the same shell, told apart by colour alone. The list now has three headings: Your call (answer, approve, merge), Agent needs a hand (send it back, or open the task) and Suggestions (start an agent, or close the issue). Each card's label is a plain state ("Ready to merge", "Reviewer asked for changes", "Nobody is on this issue", "Foxl filed this issue"), a card about a pull request says "Pull request #8" instead of a bare "#8", and one condition reads as one sentence whichever feed it came from. "Foxl is reviewing this" rows no longer appear or count - nothing is waiting on you there.
- Buttons on the Work page say what they do. "Solve" is "Start an agent". "Decline" is "Close issue", because that is what it does - it closes the GitHub issue and leaves a comment - and it now asks for a second press like Merge does, on the Not started tab too. The Answer and Retry buttons that only ever printed an error are gone: a stalled task offers "Open task", where the paid retry lives. Every Merge on the page arms with the same words, and an X says "Hide" where it only hides the row on this device and "Dismiss" where it is remembered. After a "Fix it" that went through, the card leaves instead of sitting there with the same button; a press that failed leaves it where it was. "Backlog" is "Not started".
- The Work page loads once, not in stages. Its five feeds landed on five cadences, so the Needs-you count climbed 0, 2, 5 as they arrived and the skeleton flashed on every refresh. The count and the skeleton now wait for every feed's first answer, and every tab loads and empties in the same shape. The Korean page title is 현황 (it read 이슈/PR, a name left from the deleted GitHub mirror).
Changes
- Reading messages in a channel is faster. Opening a room, and catching up after the connection blipped, were both slow - and the database was never the reason. Each read was asking our database seven separate questions one after another and waiting for each answer before asking the next, which over a network is where all the time went: the questions themselves are answered in a fraction of a millisecond. Three of the seven were just "are you allowed to see this room", asked three different ways, and two more pairs never needed each other's answers. They are now asked together. Opening a channel went from seven round trips to four and catching up went from eight to three, with the same answers to every permission question. Measured against a real server on the real database: a channel page went from 1.7x the cost of the sidebar's own list to 1.26x, and the catch-up read from 1.85x to 1.02x.
Fixes
- Links to Cursor, Zoom, Notion, Teams and thirty more apps work again. The fix for the security issue below narrowed the list of apps a link in a reply may open down to four (Obsidian, VS Code, Slack and phone numbers), which brought back the original problem in a smaller form: a
cursor://link to a file, azoommtg://link to a meeting or anotion://link to a page rendered as a normal link and then did nothing when clicked. The list now covers the editors (VS Code, Cursor, Windsurf, Zed, the JetBrains IDEs, Sublime), notes apps (Obsidian, Logseq, Notion, Bear, Craft, Things), calls and chats (Slack, Teams, Zoom, Webex, Discord, Telegram, Signal, WhatsApp, Skype, FaceTime, phone and SMS), trackers and design (Figma, Linear, Asana, Todoist, Trello), Spotify and calendar subscriptions. What stays blocked is unchanged and is the point: anything the operating system itself acts on - a diagnostic, a script host, a settings pane, a filesystem search or a remote login - plus everything that can run code. Settings > General > "Open links in other apps" still turns the whole thing off. - A Remote terminal no longer sits under a stuck "your typing is held" banner, and it stops covering the terminal with notices altogether. A cloud pane could draw its assistant's banner and then look dead a second later: a strip saying "The machine is still starting. Your typing is held..." stayed on top of a terminal that was in fact live and taking every keystroke, because the strip could only be cleared by a live output frame and an idle assistant sends none. Rather than patch that one case, the pane no longer paints transient notices over the terminal at all - a held keystroke is delivered by the server in order with nothing to announce, a session that has genuinely gone raises the Reconnect card, which is the one thing you can act on, and a cold start shows only a small wordless loading mark until the first frame arrives. The live output stream also reconnects itself if the connection drops, so a network blip or a laptop sleep no longer leaves the pane silently frozen.
- New Chat keeps what you were typing. Since v0.6.21, starting a new chat (Cmd+N, the sidebar +, the Chat row, File > New) wiped the message box: the half-written message, the images and files you had attached, and the cards for any long text you had pasted. Moving between two existing chats never touched any of that, so the one control you reach for to carry a question into a fresh chat was the one that lost it. The message box now travels with you the same way in both cases; only sending, or removing it yourself, clears it. The find bar and the previous chat's open file tabs still close on a new chat. The cards for pasted text had a second way to go missing: visiting another page (Settings, Notes, Code) and coming back kept your typed text, card numbers included, but dropped the cards themselves, so a message could carry a
#1that pointed at nothing. They come back with you now, and survive a reload like the rest of the message box. - A scheduled task could fail on exactly the days it had the most to do. A schedule that had run for months would start reporting "Model reached maximum token limit. This is an unrecoverable state that requires intervention", then work perfectly when you ran it by hand a minute later. Nothing was wrong with your schedule. Every automated run - a schedule, the Feed Generator, a reply to a Slack or Telegram mention, a request from the browser extension - was capped at a fraction of what the chat pane gives a model, and on the current default model that ceiling covers the model's private reasoning as well as its answer. So a quiet day fit and a busy day did not, which is why it looked random and why re-running it appeared to fix it. Automated runs now get the same room the chat pane gives them, and they use the thinking depth you chose in Settings instead of the model's own default.
- A schedule that hits a hiccup now retries instead of giving up. A momentary network drop or a busy moment on the model provider ended the whole run, even though the chat pane has recovered from both for months. Scheduled runs, the Feed Generator and the extension now back off and retry the same way.
- A failed scheduled run keeps its transcript. Opening one used to show a single error line with no record of what the run had already done, which matters most when it had already made real changes before it stopped. The full transcript is saved now, with the error at the end.
- The iPhone microphone prompt no longer says every recording is sent to Foxl's transcription service. That was true when the relay was the only place a phone could send audio. Notes on a phone can now transcribe on your paired computer, on your own Amazon Transcribe or speech-to-text account, or through Foxl's service, and the prompt names all of them - so a recording meant to stay on your own machines is not declined on the belief that it would be uploaded. The sentence about recording continuing in the background is unchanged.
- Internal hardening from the 2026-09-02 audit of the iOS shell: the terminal keyboard bar passes a key name into the web layer as a real JavaScript argument instead of by string interpolation (#939), and the release tooling now keeps the Xcode project's
MARKETING_VERSION/CURRENT_PROJECT_VERSIONin step with the Info.plist it already bumps - the committed pair had sat at 0.5.12 / 1877 through 23 releases (#936). - A conversation that could not be deleted now stays where it was. If the desktop refused the delete or the connection dropped part-way, the phone and the web app used to remove the chat from the list anyway, erase its offline copy and drop its pin and unread marker - while the conversation lived on at the desktop and came back on the next sync without them. The chat now stays put and a message says the delete did not go through, so you can try again.
- The search box at the top of the window now takes Japanese, Chinese and Korean input. Pressing Enter to confirm a word in the input method used to open the first search result and close the field, so a query in those languages could never be finished. Enter and the arrow keys now stay with the input method until the word is confirmed.
- Eleven low-severity findings from the 2026-09-02 security audit, across the web app, the shared UI packages and the marketing site. What you could have seen: the Notes AI stream retrying a refused request forever with no error shown (#968); a relay hiccup leaving the phone convinced the desktop tunnel was still online for a minute after a reload (#974); closing the Files cover with Escape dropping keyboard focus to the top of the page (#975); the pasted-text chip's Edit and Expand controls being 24px targets on a phone with English-only labels (#976); and a test enquiry sent from stg.foxl.ai landing in the production lead pipeline (#940). Hardening with no visible change: source citation links now pass the same URI-scheme gate as every other link in a message, so a
javascript:citation renders as text (#973); notification "Open" usesnoopener(#969); pending native login nonces are forgotten on sign-out (#967); live transcript text is HTML-escaped before it reaches the Notes editor (#971); a slide deck's inline styles can no longer carry a CSSurl()beacon (#970); and the/forge-apiCORS allowlist is pinned to the exact origins the shells load from (#972). - A pasted API key now saves even when it arrives with a stray space or line break, and a refused key says why. Copying a key from a vendor console or a password manager often adds a trailing newline; the key form sent it as-is, the server rejected it, and the form showed nothing at all. The key is now trimmed before it is sent, and if the server still refuses it the reason appears under the field.
- Signing in to AWS with SSO no longer freezes Foxl or shows your session token to other programs, and saving an SSO profile can no longer break your AWS setup. Listing the accounts and roles your SSO session can reach used to run the AWS command line once per account and wait for each one, which stopped everything else in the app - other requests, a chat in progress, the phone's connection - for up to 20 seconds per account, and it handed the live session token to those commands on their command line, where any other program on the machine could read it. Foxl now asks AWS directly over an encrypted connection, in the background, a few accounts at a time. Separately, saving a profile from the account picker could corrupt your AWS config file: a comment that happened to mention the profile name was mistaken for the real section, so the section ended up in the file twice and every AWS tool on the machine refused to start until the file was fixed by hand. Foxl now edits the file by its real sections, refuses to write anything AWS would reject, replaces the file in one step so an interrupted save cannot leave it half written, and keeps a one-time backup (
config.foxl.bak, beside the file) the first time it changes it. - The model picker now shows the model that will actually answer. Choosing a model used to be recorded on your device before the desktop had accepted it, so if the desktop refused - a provider with no key, or a tunnel that dropped at that moment - the composer named the new model while your messages were answered by the old one. The picker now waits for the desktop to confirm; if it refuses, your previous model stays selected and the reason is shown in the picker.
- A Foxl Code schedule that cannot start because three of its earlier pull requests are still open now retries and says so, instead of silently skipping the run. Scheduled runs are limited to three open tasks per account, and a pull request waiting for your review counts as open. Until now a run blocked by that limit was simply dropped: the Schedules page kept showing "enabled" with a next run in the future while nothing had happened. A blocked schedule now tries again every ten minutes until a slot frees, and the row shows why it has not started - as does any other reason a scheduled run could not begin.
- A coding assistant that committed its own work no longer loses it. Foxl Code used to decide whether a task had produced anything by looking for uncommitted files, so an assistant that committed as it went - which most of them do - was reported as "finished without changing anything" and its branch was never pushed. Every runtime now checks whether the branch is ahead of the base, pushes the commits it finds, and asks GitHub before saying nothing happened. Claude Code and Codex tasks also carry a git identity again, so the end-of-run commit cannot die on "Author identity unknown", and if a commit is ever refused the terminal says so. Codex and Kiro output is scrubbed of tokens the same way Claude Code's already was.
- Edits you type into a note are saved. Fixing a word in a note's body used to change only what was on screen: the edit reached disk only if something else - a rename, an AI summary landing - happened to save the note afterwards, so quitting the app, reloading the tab or closing the phone app brought the old text back with no warning. Typed edits are now written about a second and a half after you stop typing, and written at once when you switch notes, leave the window or close the app.
- Deleting a note asks first. Delete in a note's right-click menu used to act on the single click: the note and its recording were removed at once, with no prompt and no way back. It now asks you to confirm, naming the note - as a native action sheet on iPhone and iPad, a dialog elsewhere - and nothing is removed until you say so.
- Switching the repository filter on Work no longer leaves the previous repository's tasks in the list. If you changed the chip while the first list was still loading, the late answer was applied anyway, and every later refresh preserved those rows - so two repositories' tasks sat under a chip naming one of them until you reloaded the page. The answer for a filter you have moved off is now discarded, and its request is cancelled.
- Importing an audio file waits until the current recording is stopped. Dropping a file onto Notes, or using File > Import Recording, while a meeting was being recorded replaced the live transcript with the imported file's, and pressing Stop then saved the import instead of the meeting - which was unrecoverable. The import is now refused while a recording is running or paused: the drop zone dims and says so, and the menu command tells you to stop the recording first.
- The cost view shows what you actually spent, cached tokens included. The routing cost summary always read "0 requests, $0.00" no matter how much you had used, because it was looking at a counter nothing ever wrote to - and the maths behind that counter left out cached tokens, which on a typical agent turn are most of the bill. It now reports from the same usage records as the Usage page, with cache reads and writes counted, and it says plainly when a model has no known price instead of quietly showing it as free.
- The Ask AI sheet in Notes stays above the iPhone keyboard. Tapping the "Ask AI about the conversation" field on a phone raised the keyboard over the field and its Send button, so you could not see what you were typing. The sheet now makes room for the keyboard and the input row rides up with it, the way the chat composer already does.
- iPad no longer shows the phone tab bar next to the sidebar. On an iPad the bottom tab bar appeared alongside the open sidebar, offering the same four destinations twice. It now shows only when the window is phone-width: a full-width iPad keeps just the sidebar, a narrow Split View pane still gets the bar, and it follows a rotation or resize live.
- A daily Foxl Code schedule set to
24:00no longer saves as a schedule that never runs. Several time pickers write midnight as24:00; the server accepted it, showed the schedule as enabled, and then never fired it, because that time could not be turned into a next run. A time outside00:00-23:59is now refused when the schedule is saved, with the allowed range in the message, and a schedule can no longer be saved or re-enabled in a state where it has no next run - pausing it or fixing the time are the two ways out. - The daily cap on unattended Foxl Code work now counts real money. The cap was compared against a fixed per-wake-up estimate, so it bounded how often the autonomous loop woke and said nothing about what the tasks it started actually cost - a $5 cap could not stop a day of $50 in task spend. The loop now reads what the day's tasks have really spent (settled and still reserved) before every run and goes quiet at the cap, and Settings shows that real figure. If the ledger cannot be read, the loop skips the run rather than spending against an unknown total.
- Search no longer rebuilds its whole index every time Foxl starts when your conversations are not in a Latin script. Foxl decided whether the search index was already built by looking for a word beginning with the letter "e", and a Korean, Japanese or Chinese history has none - so it concluded the index was empty and re-indexed every message on every launch, more slowly the more history you had. It now asks the index how many messages it holds, which works in any language and also catches messages that were saved while the index was unavailable.
- Deleting a message, resetting a chat, or archiving one now tells you when it did not go through. If the desktop refused the request or the connection dropped part-way, the app used to apply the change anyway - the message vanished, the chat emptied, or it moved into or out of the archive - and the next reload quietly put everything back. Nothing changes now until the server has agreed, and a refusal shows a message so you can try again.
- A search that could not run now says so, instead of showing "No results found". When your desktop is asleep or the connection has just dropped, searching your conversations from the phone or from the title bar looked exactly like a search with no matches. It now tells you the search is unavailable and to reconnect and try again.
- Every model id in the catalog now resolves to its own model. Seven of the provider-specific ids the catalog lists for its own models - the dated Anthropic ids and the versioned Bedrock ids for Opus 4.6, Sonnet 4.6 and Haiku 4.5 - were not recognised by the catalog's own lookup, so a request made with one of them was treated as an unknown model. For Haiku 4.5 that meant the thinking settings the model rejects. The lookup now knows every id it lists, and a name that merely ends in a model id no longer inherits that model's settings.
- Removing a saved API key now tells you when it could not be removed. The key row used to switch to "not configured" the moment you pressed Remove, even when the server had refused and still held the key - it came back on the next reload. The row now stays in place and the reason appears under it, so you know the key is still there.
- Deleting a Foxl Code task now asks first. Right-click, Delete (or arrow-down and Enter) on a task removed it, its transcript and its whole event history in one click, while the neighbouring Delete all already asked. Both places a task can be deleted from - the task menu and the open task's own menu - now confirm with the task's name, using the same native sheet on iPhone.
- Provider options that fail to load now say so and offer Retry. In Settings, the routing, model and cost controls of an OpenAI-compatible provider used to render as a blank area when they could not be fetched - for example over a flaky connection from the phone - with nothing to say why. There is a spinner while they load, and an error with a Retry button if the load fails.
- A pause or a cough during an on-device recording no longer makes the next sentence overwrite the previous one. When the on-device engine was handed an utterance it could not turn into words - a short noise, or a rare engine error - it said nothing about it, and the sentence after it was numbered as if it were the same utterance. On screen that meant the new sentence replaced the earlier line instead of being added after it, so the note came out one sentence short with no gap and no warning. Every utterance is now counted, whether or not it produced words.
- Syncing or installing skills no longer freezes the app. Pulling the skills library and installing a skill from skills.sh used to hold up the desktop's server for their whole duration - up to two minutes on a slow network - so chat replies stopped mid-sentence, terminals stopped echoing, and the app could look as if it had crashed. Both now run alongside everything else and the rest of the app keeps working. The skills.sh installer is also pinned to one exact version of its command-line tool instead of whatever the package registry serves that day, and a package name must now be exactly
owner/repo. - The find bar inside a note is translated. Pressing the find shortcut in a note showed "Find in note" in every language, because that label was missing from all ten Notes translations and the English fallback was the only thing that could render. The Notes translations are now checked in CI the same way the rest of the app's are, so a label missing from one language fails the build instead of shipping in English.
- Searching your conversations on Windows now finds what it should, treats
%and_as ordinary characters, and no longer errors on a long paste. Windows searches without the fast index, and that path took your query too literally:AKIA*or-awsfound nothing even when a message plainly contained the word, while a query with a%in it matched every conversation you have. Punctuation is now handled the way the indexed search handles it, so both give the same answer, and pasting several paragraphs into the search box returns results instead of an error. - A quote or a line break in your profile no longer wipes it on save. On the desktop, saving a name or bio containing a
"or a new line wrote a profile file the app could not read back correctly, so the form closed on "Saved" and the next visit showed the text mangled or missing. The profile is now written in a form that survives any character, and if the desktop refuses the save you are told instead of the form closing. - Notes tells you when the export to your folder is failing. If the folder you chose for auto-saved notes was renamed, unmounted, full or no longer writable, the note still appeared in Foxl and nothing was written to the folder - with no message anywhere. The first failure now shows a notice naming the folder, and Notes settings keeps a marker beside the folder until an export succeeds again.
- Permanently deleting one chat from the Trash now asks first, like Empty trash does. The small trash icon on each row - a few pixels from Restore - used to hard-delete that chat the instant it was clicked, with no way back, while the bulk Empty trash button right below it showed a confirmation. Both now confirm, and the single-chat dialog names the chat it is about to remove.
- Screen readers announce the sidebar, chat and note find controls in your language. The Home button, the product switcher, the connection dot, a conversation's delete and stop buttons, the "generating" indicator, and the previous / next / close buttons of the find bars in a chat and in a note all named themselves in English whatever language the app was set to. Each is translated now, along with the chat find box's "Find..." placeholder.
- Connecting Microsoft 365 or Slack no longer hangs when its sign-in port is already taken. Foxl receives the sign-in through a small listener on your own machine. If that listener's usual port was busy - a second Foxl window, another app - Foxl quietly moved to the next port but still told Microsoft or Slack to deliver the sign-in to the old one, so it went nowhere and Settings sat on "connecting" for five minutes before failing with no explanation. The provider is now told exactly where Foxl is listening, and if no port is free at all you get an error immediately instead of a wait.
- A database upgrade step that fails no longer silently cancels the steps after it, which could leave per-conversation usage permanently broken. Foxl adds new columns to its local database as it starts, and several of those were grouped together so that one failure skipped the rest - with nothing recording how far it had got, so later starts skipped them again instead of finishing the job. On an affected install the per-chat token and cost figures stopped working for good, with no error anywhere. Each step now runs on its own, retries on the next start, and says so loudly if it fails for any reason other than already being done.
- The copy, share, edit and regenerate buttons under a message have names for screen readers. They were icon-only with no accessible name, so VoiceOver read four bare "button"s under every message with no way to tell which one regenerates the reply. Each is named now, in your language, and the copy button announces "Copied" while its check mark shows. The one tooltip in that row (Report) is unchanged.
- The audit log now records what the agent ran and what was blocked. It had been empty since it shipped: the code that wrote it sat on a path the agent never took. Every tool call now lands there - approved, auto-approved or refused, with the reason it was refused, how long it took, and which agent and conversation ran it. Keys and passwords in tool arguments are redacted before they are written.
- Importing your settings from Claude Code or OpenClaw works when the file contains a web address, and the report now says what was actually imported. The importer treated the
//inside anyhttps://link as the start of a comment, so a configuration with a single URL in it could not be read - and the summary still said "Imported N items". The report now separates what was written into Foxl from what was only found and noted for you to set up by hand, so the number you read matches what is on disk. - Every text field accepts Korean, Japanese and Chinese input without submitting on the IME's Enter. In those input methods Enter finishes the word you are composing, and renaming a conversation, a speaker, a note or a terminal pane - along with about a dozen other fields - treated that keystroke as "done" and saved the half-typed text. Those fields now wait for the Enter you press after the word is committed, the way the chat composer already did.
- Pressing Reconnect on a dead Foxl Code task no longer keeps it alive forever. A task whose agent had gone away was supposed to be closed out automatically after 14 days with no progress, but each failed Reconnect attempt was recorded as if the agent had done something, which restarted that clock - so the tasks people tried hardest to recover were exactly the ones that never got cleaned up, and the background loop behind them stayed pinned awake. Failed reconnects and Foxl's own status notes are no longer counted as progress; only the agent's real activity is.
- Subagent rows in the sidebar and session chips in the phone terminal can be reached and opened from the keyboard. Both responded only to a pointer: Tab skipped the row and landed on its small stop / close button, so with an external keyboard or a screen reader there was no way to open a running agent's conversation or to switch terminal sessions. Each row is a real button now, with its stop / close control beside it rather than inside it, and the sidebar's stop button appears on keyboard focus as it does on hover.
- When a request to the local server fails, the app now shows what went wrong. A failing request used to come back as a web page rather than as an answer the app could read, so the screen reported a parsing problem instead of the actual reason - and a request the server had merely refused was reported as a crash. Failures now carry their real reason and their real status, and internal details stay in the log instead of being sent back.
- Typing into a cloud terminal from two places at once no longer loses a keystroke. If two writers reached the same pane while its machine was still starting - the terminal and an agent tool, or two devices - both were told the typing was held, but only one of the two actually ended up in the hold and the other was silently dropped. Each held keystroke is now added to the hold in one indivisible step, so both land, in order.
- The hold for typing into a starting cloud terminal is now bounded in bytes, and a paste that does not fit says so. The limit counted characters rather than bytes, so held CJK or emoji typing could grow to two or three times the intended size, and a single very large paste was not limited at all. The oldest bytes are dropped first and never through the middle of a character, and the answer to a paste that had to be cut now says how many bytes were dropped instead of reporting it as held whole.
- A refused local Code request no longer signs you out of the app. When the web app or the phone asked this computer about its coding tasks before your session had finished attaching, the desktop answered with the same status the account server uses for an expired sign-in, and the app took it at its word and logged you out. The desktop now reports that request as incomplete instead, so a cold start or a token refresh in flight never costs you your session.
- Android now says notifications are not available yet, instead of pretending they are on. Push notifications are not built for Android - so an Android user who turned them on received nothing, forever, while the app showed the phone as registered. The Notifications screen on Android now says notifications are not available there yet (they work on iPhone and iPad), and the app no longer registers an Android device that could never be delivered to.
- Stop now stops the helper agents too. Pressing Stop while the assistant had delegated work to subagents ended the visible reply but left those subagents running commands in the background - and when they finished, the conversation you had just stopped started a new turn on its own to summarize them. Stop now halts every subagent the conversation started, and nothing restarts the conversation afterwards.
- Microsoft 365 no longer disconnects itself when the agent does several things at once. Microsoft issues a new sign-in token on every renewal and retires the old one. When two tool calls ran at the same moment - reading email and checking the calendar in one turn, say - both renewed at once and Foxl could end up keeping the retired token, so the next renewal failed and the connection had to be set up again from scratch. Renewals are now shared between whatever is running, and if Microsoft does reject a sign-in for good, Settings says so on the Microsoft 365 row instead of every request failing the same way.
- Schedule cadences are written in your language and your clock. "Every Monday at 9:00 AM" appeared in English on every Schedules row, card and preset whatever language the app was set to, with a 12-hour AM/PM clock even where the language uses a 24-hour one. Weekday names and times now follow your language and its clock, and the words around them are translated.
- Suggestions arrive in your language even when another language was being generated. If two Foxl windows, or the app and the phone, asked for the greeting and the starter suggestions at the same moment in different languages, the second one was handed the first one's language and kept it for five minutes. Each language is now generated and cached on its own, and a request for a language Foxl does not ship is refused instead of quietly answered in English.
- A Foxl Code terminal could open on nothing for a retried task, and could point at the cloud for a task running on your own machine. Reattaching a retried task's terminal connected to a session that never existed, so the pane stayed blank; and a task running on a machine you connected could be handed a cloud terminal that machine has no session for. The terminal now uses the task's real session, and a task on your own machine no longer opens an empty cloud pane.
- Resizing a Foxl Code terminal now ignores an out-of-range or malformed size. A pane width or height sent from the browser reached the cloud runtime unchecked, and resizing a finished task or sending a malformed request could return a server error. Sizes are now clamped to sensible bounds, a resize on a task that is no longer running is refused cleanly, and a malformed request no longer errors.
- "shared/notes.md" and "Shared/notes.md" are the same shared file. The workspace recognised the team's Shared folder only when it was spelled with a capital S. On a Mac or on Windows, where folder names are not case-sensitive, a path typed as
shared/...quietly became a private file on that one computer while looking like a shared one. Any spelling of the folder name now means the shared folder, everywhere in the workspace. - The Feed Generator's
watchsetting is now read the way the examples write it. Awatch:block in HEARTBEAT.md written as nested lines - the form every example shows - was read as an empty setting plus three unrelated keys, so the "only wake the agent when something changed" check never ran while it looked configured, and saving any Feed Generator setting rewrote the file in that broken shape. Nested blocks are read correctly now, a trailing# commentis a comment, and a block Foxl cannot read is skipped with a log line naming the file and the line instead of being silently misread. - A Foxl Code task no longer fails because its test suite could not start. When a repository's tests need dependencies that were never installed, running them on a fresh checkout stops before a single test executes - and on the Codex, Kiro, Cursor, Hermes and OpenCode assistants that was reported as a failing test suite, so the pull request was withheld and the task was marked failed, while the same repository and the same prompt on Claude Code opened its pull request. Every assistant now installs the repository's dependencies before running its tests, and a suite that could not start is reported as inconclusive rather than red. A suite that runs and fails still blocks the pull request, as before.
- Foxl Code never puts its own dependency install into your pull request. The install that runs before the tests happens in the same checkout that is committed a moment later, so on a repository that does not ignore
node_modules(or a Python virtualenv, or a build output) the harness's own files could have landed in the pull request you were asked to review. Whatever the install creates is now kept out of the commit, on every assistant, and a directory your repository ships on purpose is left exactly as it was. - A file sent with no caption no longer shows up as "Message deleted", and deleting a message now takes its attachments with it. Channels decided whether a message had been deleted by looking for an empty body - which is also what a screenshot or a document dropped into the composer with no text looks like, so your own attachment rendered under an italic "Message deleted" line. Deleting a message, meanwhile, only blanked its text: the tombstone kept its file chips, each one still a working download for everyone in the room. The relay now says explicitly when a message was deleted, a deleted message shows no attachments, and its files are removed from the workspace the same way a delete from Files is - billing stops that night and the bytes follow after the usual grace window.
- A photo or file sent over Telegram reaches the agent instead of vanishing. Foxl acknowledged an attached image or document, then answered as if you had sent only the word "Image" - the file was fetched and thrown away. Attachments are now saved into your workspace inbox and handed to the agent so it can look at the photo or read the document, and one it cannot use (too large, or a type it cannot open) gets a reply that says so instead of a confident answer about nothing.
- A long Foxl Code conversation no longer jumps back to its beginning when you open it somewhere else. Past 200 messages, opening a task on a second device (or after clearing the browser's storage) showed the first 200 messages and dropped the most recent ones - and because that view is also what your next message is answered against, the agent carried on as if the latest exchanges had never happened. The transcript now opens at its newest messages, and a "Load earlier messages" button at the top walks back through the rest.
- Starting an agent that cannot start now says so immediately. "Start agent" used to answer "Agent started" before the run existed, and when no model could be built the failure was only ever announced on the live connection - a phone reaching the desktop through the tunnel, or a window that reconnected, saw a success for a run that had died at once. Worse, that failed start left the agent marked as running, so every later attempt was refused with "Agent already running" until the app restarted. The request now waits for the run to exist and reports the real reason when it cannot, and a failed start no longer blocks the next one.
- Foxl Code's assistant no longer carries every tool into every turn, so a chat question can no longer end in a GitHub issue you did not ask for. Until now only the PR reviewer had a limited toolset: a plain chat message, the autonomous loop's advance tick and its idle survey were each handed all 30 tools, including near-duplicates and two that look alike but do different things - one files an issue in your tracker, the other also spends the loop's own issue budget, and a chat turn spending that budget could silence the survey for a week. Each kind of turn now gets only the tools that turn is for: chat keeps its wide set but cannot touch the loop's budget and files an issue only when you explicitly ask for one, the advance tick drives your queue and cannot file issues at all, and the idle survey keeps exactly what surveying needs. The tools that overlap now also say which one to use when.
- foxl://chat links now open the conversation in the desktop app. Copying a link to a conversation and clicking it later just focused the window and did nothing on the desktop, even though the same link worked on the phone. The link now opens the right conversation (or a new chat for foxl://chat/new).
- Turning automatic updates off no longer strands an update you already downloaded. The "Download updates automatically" switch also quietly disabled installing a downloaded update when you quit - so an update that had finished downloading was never applied, and the app kept reporting it as pending. The switch now only controls automatic downloading; an update that is already downloaded still installs when you quit.
- A workspace whose plan renews on the last day of the month is no longer billed for storage or Channels hours before its credits arrive, and an unpaid storage bill no longer blocks uploads for a month. For a renewal on the 29th, 30th or 31st at a daytime hour, the monthly charge ran on that day's midnight pass - up to fourteen hours before the renewal grant landed, exactly when the balance is lowest - so it could fail, mark the workspace past due and refuse every upload while the owner was fully paid up, and nothing looked at it again until the next renewal. The charge now waits for the first pass after the renewal instead. And a charge that did fail for lack of credits is now retried every day, so a top-up clears the past-due flag on the next pass rather than at the next anniversary.
- A cloud terminal that lost its live connection reconnects instead of going quiet. If your laptop slept or the network dropped while a task's terminal was open, the pane could stop updating for good: the agent kept working, but nothing on screen said the connection was gone, and only a reload brought it back. The terminal now keeps reconnecting for up to ten minutes, reconnects at once when you return to the tab or the network comes back, and if it still cannot get through it says so and offers Retry.
- No estimated dollar costs or upgrade prompts appear while paid plans are hidden. A few screens still showed an estimated dollar cost for your model usage, worked out from a price list that was months out of date, and an unused Notes dialog still carried an "Upgrade to Pro" button. Those dollar estimates and the dialog are gone from the app while Foxl runs as a free project; the usage screens keep their token and message counts.
- An expired Foxl Code session now renews itself instead of signing you out of everything. Foxl Code treated any "unauthorized" answer from its server as the end of the session and dropped the whole app - Agent, Notes and Code - to the sign-in screen, even though a valid refresh token was still on the device. An access token lasts 12 hours, so this happened on a normal day's use, and most often on the first chat message sent after the expiry. Foxl Code now refreshes the session and retries the request once (a chat message is also sent with a renewed session in the first place), and signs you out only when the refresh itself is refused.
- Runs that happen while nobody is watching have a budget now. A heartbeat tick, a scheduled task or a reply to a channel message runs unattended, and until now nothing bounded one: the "maximum runtime" only noted that a run was stuck and let it keep going, and there was no limit at all on how many tools it could call or how much text it could generate. Each such run now stops at 40 tool calls, 24,000 generated tokens or its maximum runtime (10 minutes unless you changed it), whichever comes first, and a run that hit a limit is recorded as "budget_exceeded" with the reason rather than as a normal completion. A heartbeat run also can no longer hand its work to a separate background agent, which would have run with none of these limits; scheduled tasks and channel replies were already kept from doing that. The heartbeat's limits can be tuned in HEARTBEAT.md (
maxToolCalls,maxOutputTokens,maxRuntime), within a fixed ceiling. - Switching models mid-chat could silently wipe the conversation from the agent's memory. After a model switch (or a stop, an error, or an app restart) the desktop rebuilds the agent from the saved transcript, and it restored the FULL stored tool output every time - including screenshots that
view_imagehad returned as base64 text, around 100k tokens a picture. Once a conversation's saved history was larger than the new model's context window, the model refused it, the recovery trimmed the history down to the message you had just typed, and the reply carried on as if the chat were new: nothing in the window said so. Measured five times in one evening on v0.6.28. Now the restored history is sized to the model it is being restored for (inline images are dropped as text, then the oldest tool outputs are shortened, then the oldest turns are left out with a note that says how many), the chat uses the same proactive summarizer the autonomous agent has had since v0.6.19 instead of a last-resort trim, and every compaction shows up in the transcript as a divider, the way a model switch already does. - The agent can actually see the images it looks at.
view_image(and thegenerate_imagepreview) handed the model its picture as a base64 string inside text - around 100k tokens that no model can read as an image, which is also what filled the conversations above. Both tools now return a real image block: the model sees the picture for roughly 1.5k tokens, the thumbnail in the transcript is unchanged, and on the two catalog models without vision the image is replaced by a one-line note instead of a refused request. - A shell command that runs out of time is now stopped completely, and a background command no longer runs forever. When one of the agent's shell commands hit its time limit, only the shell itself was stopped: a pipeline or a chain of commands kept running underneath, invisibly. And a command started in the background had no time limit at all. Now the whole command - every process it started - is stopped when the limit is reached, background commands get their own limit (an hour unless the agent asks for more), and the process list shows each command's deadline and how long it has left. And when Foxl's agent service stops - quitting the app, or restarting for an update - any command the agent still had running is stopped with it, instead of being left behind with no time limit.
- Notes: pressing Summarize saved the note dozens of times and left the transcript on disk instead of the summary. The summary streams in token by token, and every token was treated as a finished summary: the note was written to disk, a new Word or Markdown file was built and saved, and a "Saved" toast fired - once per token, for the whole stream. Because the note's title is derived from the summary, the file name also changed as the first sentence grew, so the auto-save folder filled with partial copies under different names, and the transcript-only file written when the recording stopped stayed behind under its old title, which is the file you opened. Now the summary is written back once, when it is complete, and its export replaces the file from the previous title - so one note is one file, and that file holds the summary.
- Two agent pull requests from one plan can no longer merge unattended on top of each other when they both add a database migration with the same number. Each of those pull requests is green on its own and GitHub reports no conflict, because two new files never conflict - so nothing in the unattended merge path could see that the combined result numbers two migrations identically, the exact shape a human merge train once had to catch by hand. Foxl Code now checks a pull request against its siblings before merging it unattended and holds the merge when the numbers collide. The hold shows up in Needs you, and on your phone, as a held merge rather than a refused one - GitHub was never asked - so you know to renumber one of the two migrations before merging (a migration that has already been applied must keep its name). Everything that does not add a migration merges exactly as before.
- A schedule that keeps failing now tells you, keeps trying, and can be resumed with one tap. After three failures in a row a schedule used to be switched off silently: nothing on the Schedules page said why, no notification was sent, and it never ran again until someone noticed the silence. It is now shown as "Paused after 3 failures" with the last error and a Resume button, you get a notification when it happens, and instead of stopping for good it retries with a growing delay (an hour, then six, then daily) until a run succeeds or you resume it.
- Limiting a shared folder to specific people can no longer hide it from the person doing it, or from any workspace administrator. Restricting a folder without ticking yourself used to save fine and then drop the folder, and every file in it, from your own sidebar on the next refresh - with no row left to reopen the sharing dialog from, nothing saying why, and the storage still counting. Now whoever limits a folder always stays on its list with edit access, the dialog shows that and will not let you un-tick yourself, and a workspace owner or admin can see and open any restricted folder in their workspace even when they are not on its list - which also brings back folders that were already lost this way. The first time each day an administrator reaches a folder that way, it is recorded in the workspace's audit log.
- Pressing Enter in Foxl Code while a message is still sending keeps your draft, and Stop still stops. Typing a follow-up while the assistant was working and pressing Enter used to empty the box and send nothing - the text was simply gone. Enter now does nothing until the current turn finishes, so what you typed stays in the box and goes out when you press it again. Tapping Stop with a follow-up in the box used to empty the box as well, instead of stopping; it now stops the reply and leaves your text where it was.
- Long Foxl Code chats stay smooth while a reply streams. Every streamed token used to redraw every earlier message in the transcript, so typing in the composer stuttered and the page scrolled jerkily the longer the conversation got - on a phone it became unusable well before the Agent chat did. Earlier messages are now left alone while the reply comes in, as they already were in the Agent chat.
- Speaker separation could not finish installing, and said it had. Installing the on-device transcription engine downloaded the two speaker models and then saved neither of them, so Notes kept offering to install them and every attempt downloaded the same 33 MB again. The download now saves its files, and if that part fails the installer says so instead of reporting success - transcription itself keeps working either way, just without speaker labels. The Install engine button also stays on screen while speaker separation is missing or an install has failed, so the retry is one press away; before, it disappeared as soon as transcription itself was ready.
- The Foxl Code tier table can no longer miss a new Claude model. The list of Claude models a Pro, Ultra or Enterprise account may run a Foxl Code task on was typed by hand, and Fable 5.1 - added to the model catalog on September 1 - was not on it. The table is now derived from the catalog itself, so the next Claude model cannot be left off. This is hardening with no user-visible change today: tier limits are not enforced while Foxl is free, and the Foxl Code model picker does not offer Fable 5.1 yet, so nobody could have been refused. (#943)
- Gemini 2.0 Flash is no longer offered. It was the one model in the catalog with no price, so a chat on it with your own Google key was recorded as costing $0.00. Google's own pricing page no longer lists any 2.0 model, so there is no honest number to give it and the row has been removed; Gemini 2.5 Flash and 2.5 Pro remain. A saved selection of the old id moves to Claude Sonnet 5 (or the first model your plan can run) the next time the chat opens; Google itself shut Gemini 2.0 Flash down on June 1, 2026. (#944)
- A storage or Channels charge can no longer deduct credits while its receipt reads $0.00. The credit amount and the dollar amount were rounded separately, and at the smallest fractions of a gigabyte-month or member-month they could round in opposite directions - one up to a deduction, the other down to zero. The dollar figure is now derived from the rounded credits, so the two always agree. (#945)
- Hardening with no user-visible change today: the pre-flight task-cost estimate clamps out-of-range inputs instead of producing a negative or infinite budget, the credit gate refuses rather than admits a task when handed a non-numeric cost, count or balance, and a negative token count from a provider is treated as corrupt rather than billed as a refund (#959, #757). Internal fixes to the status page, CI and release tooling: outbound status webhooks no longer follow redirects (#941); the public error redaction now also hides internal hostnames, dashed ids, database table and column names, Lemon Squeezy keys and IPv6 addresses (#947); the release gate reads the published state back after publishing (#890); the iOS workflow declares least-privilege permissions and records the commit of its out-of-repo plugin (#889); the Discord bot image runs unprivileged and every base image is pinned by digest (#887); a stale "ephemeral runner" comment was corrected (#886); and the workflow-injection gate now also catches
github.event.*,github.head_refandgithub.actor(#888).
Security
- One account can no longer fill the shared database through push registration. The phone's push-token registration accepted a token of any length, any number of times, with no limit on how often it could be called, so a single free account could write enough oversized "tokens" to reach the database's hard size cap - at which point every write in the service fails, for everyone, not just push. A token is now checked against the shape real APNs and FCM tokens have, each account keeps at most its 20 most recently registered devices, and the route shares the same per-address rate limit as sign-in. A phone registering normally sees no difference.
- Pet gifts can no longer be used to send unlimited email to any address. Sending a pet to a friend emails them an invite, and the only limit was on how many gifts were waiting to be claimed - taking a gift back freed that slot at once, so one account could send, take back and resend to any list of addresses without end, each time putting 200 characters of its own text into a message from our domain. An account can now send at most 10 gifts a day and can mail a given address once a day, whatever became of the earlier gift, and sending or taking back a gift shares the same burst limit as signing in. Claiming a gift is unchanged.
- Removing someone from a workspace now closes their open Channels connections immediately. Their access already ended the moment an admin removed them, but a room they still had open kept streaming new messages to them until they closed the tab, and they stayed in any voice huddle they had joined. Removal now ends those live connections and drops them from the call at the same time.
- One account can no longer flood the moderation queue or fill the shared database with content reports. Reporting a message or a person had no ceiling: a script could file an unlimited number of reports against made-up targets, each with a maximum-length note, and every one became a permanent row in the database all Foxl accounts share and another line in the only review queue there is. Reports are now capped per account - at most 50 awaiting review at a time and at most 100 in any 24 hours - and the route goes through the same per-address limiter as signing in. A note longer than 2000 characters is refused with a clear error instead of being silently cut short (the app's own report form already stops at 2000, so nobody typing in it will see this). Reporting the same thing twice still just updates your earlier report.
- Connected-account tokens and skill secrets are now encrypted on disk, and the assistant can no longer read them back. Signing in to Microsoft or Slack, or typing a skill's API key on the Integrations page, used to store that credential as plain text in the same local database the assistant searches with its memory tool - so a web page the assistant read could instruct it to "recall" the entry and send it somewhere. Those entries are now encrypted with the same per-install key that already protects your provider API keys, the memory tools refuse to read or write them, and the memory viewer shows only their size. Existing connections keep working and are re-encrypted the first time they are used; there is nothing to redo.
- Someone holding one of your signed-in sessions can no longer keep your password sign-in locked, or guess your current password without limit. Changing or removing a password asks for the current one, and a wrong answer counted toward the same lockout that protects sign-in - but those two requests never checked whether the address was already locked. So each wrong guess made during a lockout pushed its end another 15 minutes out, and the guessing itself was never refused. Both requests now refuse while the address is locked, with the same answer sign-in gives, and a failure that arrives during a lockout no longer moves when it ends.
- Blocking someone now silences them on your phone too. Blocking a person already hid their messages from your timeline, but the notification sent for each message had its own recipient list that knew nothing about blocks - so a blocked person could still put their name and the first 140 characters of every message on your lock screen, and your unread badge kept counting them, with muting every colleague as the only way out. Blocks now apply to notifications in both directions, in channels and in direct messages that existed before the block, and to huddle rings, so a person you have blocked can no longer ring your phone. Unblocking restores the notifications.
- A machine's address is checked no matter how the machine was registered. Foxl Code refuses to send a task - which carries a GitHub token and your model credential - to a private, loopback, link-local or cloud-metadata address, or over plain http. That check only ran for machines added as an existing host; a machine added as a launched EC2 box could be given any address at all. It now runs for every machine, runs again at the moment a task is dispatched, and a machine whose stored address fails it is marked failed with the reason shown in the Machines pane instead of being used. The deprecated IPv6 site-local range and the short names cloud providers use for their metadata service are refused as well.
- The desktop app no longer hands its connection token to pages on local dev ports. The packaged app's local API accepted cross-origin reads from the Vite development origins (localhost on ports 5173 to 5175), and the page that bootstraps the app carries the token that unlocks every local API route, so a page served from one of those ports could read it. Those origins are now admitted only in development, and the app shell is never readable cross-origin. Starting the agent from a plain GET link (
/api/goal?q=...) is gone too: it is a JSON POST now, which a foreign page cannot send without a CORS preflight. - A crafted link on our own relay can no longer sign you into someone else's account. An old sign-in hand-off address on relay.foxl.ai accepted login tokens from the link itself and passed them straight into the app, so a link that looked first-party could quietly log a visitor into an attacker's account, where everything they then wrote could be read by that attacker. Nothing has used that address since v0.1.3; it is gone, and a check now keeps any relay route from accepting sign-in tokens out of a link again.
- Linking a GitHub account to Foxl Code now finishes only in the browser that started it. Before, the link was completed by the page GitHub sent you back to, and a crafted link could bind the GitHub identity of whoever clicked it to someone else's Foxl account - which Foxl Code treats as proof that you own your repositories. GitHub now hands your browser a one-time code that only your own signed-in session can redeem, within five minutes, once. If pressing Link GitHub ends with "started from a different Foxl account", press it again from the account you are signed in to.
- A Foxl Code agent can no longer approve its own pull request, and every approval names the exact commit that was reviewed. The credential a cloud agent carries could reach the endpoint that records a review verdict - the second of the two gates before an automatic merge - so a prompt injection hidden in a README or an issue could have had an agent mark its own PR as reviewed, or a sibling task's. Only the independent reviewer's credential can report a verdict now, and only for the one task it was asked to review. Separately, a verdict used to attach to whatever commit was current at the moment it arrived, so a push made while the reviewer was still reading could have been merged as if it had been reviewed. A verdict is now refused unless it names the commit that is still the head, and the newer commit gets its own review.
- A sign-in link now only completes in the browser that started it. On app.foxl.ai and code.foxl.ai, a specially crafted link could quietly sign your browser into someone else's account, so everything you did next - chats, notes, Code tasks, shared-folder edits - landed there and was readable by them. Starting a sign-in now leaves a one-time secret in your browser that the sign-in service hands back, and a link that cannot present it is ignored, removed from the address bar, and explained on the sign-in page. One visible consequence: a magic link opened in a different browser or on a different device than the one that requested it no longer signs that other browser in - request the link from the browser you want to use.
- Removing someone from the Foxl Code beta now also stops the work GitHub triggers on their behalf. The beta allowlist closed the app and the scheduled loops for a removed user, but a push to one of their task branches, an approval on a pull request, or a finished build could still make Foxl mint a fresh GitHub token, spend a reviewer turn, wake their coding agent, or merge to the default branch for them. Each of those paths now checks the same allowlist first and records that it declined, so a user the operator cut off is cut off everywhere. Nothing changes when no allowlist is set.
- Android backups no longer carry your sign-in or your meeting notes off the phone. Android's automatic backup to Google Drive, and the copy-everything-to-a-new-phone transfer, used to include Foxl's private data - so a backup restored onto another device arrived already signed in to your account, with every meeting transcript, summary and recording. The app now tells Android to leave all of that out. Your notes stay on the phone that recorded them, and a new phone asks you to sign in.
- The desktop window now hands a clicked link only to your web browser, and a page can no longer write or delete files outside the folders you chose. Three holes in the desktop app's bridge to the operating system, found by the 2026-09-02 audit: a link in a chat reply or a tool result could name a non-web scheme (
smb:,ms-msdt:,file:) and one click handed it to the OS; a Notes recording id containing..could read, write or delete a file outside the recordings folder; and a Notes auto-save export accepted any path at all. Now only http(s) links leave the window, recording ids must be plain identifiers, and an auto-save export must land inside the folder you picked. Nothing to configure; a refused action is logged and reported instead of being quietly redirected. - A Microsoft or Slack sign-in now only completes for the sign-in this desktop started. An unused server route could finish an integration sign-in from details supplied by whoever called it, which would have let another account be connected as yours. The route is gone; connecting Outlook or Slack works exactly as before, through the browser window Foxl opens.
- Signing out on a shared phone now stops that phone receiving your notifications. Until now a phone's push registration stayed attached to every account that had ever signed in on it, so if you signed out of a shared iPad and someone else signed in, your message previews and other alerts kept arriving on their lock screen - indefinitely. A phone now belongs to one account at a time: signing in takes it over, signing out releases it, and existing duplicate registrations are cleaned up when the update rolls out.
- The Foxl Code assistant running on your own computer now obeys the same shell rules as the Foxl agent. When you chat with Foxl Code on this machine, it can run shell commands for you - and until now those commands skipped the protections every other Foxl shell command goes through: your blocked-command rules were not consulted and no approval prompt could appear, even though the same command typed at the agent got both. That mattered because this chat is reachable from your phone and from app.foxl.ai through your desktop connection. Both are applied now, from the rules you already have; nothing to turn on, and if you have approvals switched off nothing about your day changes.
- A folder name can no longer change what Codex trusts. When Foxl opens a Codex pane it writes a small "this folder is trusted" entry into your Codex configuration, so the pane does not stop on a first-run question. A folder whose name contained a quote character could append settings of its own to that file - including trusting your entire disk for Codex sessions you start yourself, outside Foxl. Such a name is now refused, with the reason logged, and Codex simply asks its own question instead.
- Microsoft 365 requests now encode every id and search value before it reaches the URL. A message, folder, event or task id chosen by the assistant - which can be steered by text inside an incoming email or invitation - could previously rewrite the request itself: widen a mailbox read from 50 headers to hundreds of full message bodies, drop the limits Foxl had set, or point the call at a different Microsoft Graph route, all with your signed-in Microsoft account. Every value now stays inside the path segment or query parameter it was meant for, and an empty id is refused instead of being sent.
- Deleting your account now removes everything the service kept under your name, including any coding-agent credential you had uploaded. Account deletion worked from a hand-written list of tables, and that list had fallen behind the database again: twenty-four kinds of per-user data survived a deletion. The most serious was the Claude or ChatGPT subscription credential a Foxl Code cloud machine runs with, which stayed stored indefinitely with no account attached; also left behind were the encrypted copies of assistant replies parked for a suspended phone, your notification badge count, your daily usage rollups, the questions the coding loop had asked you, your remote machines, and your channel memberships, mentions and reactions. All of it goes with the account now. Channel messages you posted stay with the workspace, as before, and a check in the build fails whenever a new kind of per-user data is added without a decision about what happens to it on deletion.
- A Foxl Code run can no longer pick its own compute discount, and a garbled end-of-run report can no longer leave a task stuck on "running". The coding container's finish report carried an "idle time" percentage that the relay multiplied into the compute charge, even though no container measures it (every one sends the same fixed value) and anything steering the agent could send a different one: a report of 100% idle cut the processor part of the bill to zero, and a non-numeric value crashed the charge, so nothing was billed and the task stayed "running" forever. The relay now bills compute from what it knows itself - its own clock, the machine profile and a fixed idle fraction equal to what every honest run reports, so honest charges do not change - keeps the report only as an audit note, and always records the task's final status even when the report is unusable.
- Opening a sign-in link on a second device no longer signs the first one out. When you asked for a magic link on one device and opened it on another - the common case of requesting it on a laptop and tapping it on your phone - the new session was mistakenly filed under the first device's identity, which quietly logged that device out at its next refresh and showed the wrong device in your session list. The redeemed session now belongs to the device that actually opened the link, and the device that only asked for it stays signed in.
- Sign-in links and callbacks are no longer cacheable. Every response that carries an access or refresh token - the magic-link redirect and deep-link page, the Google, Apple and GitHub callbacks, and the token-refresh and password-login responses - now sets
Cache-Control: no-store, so a shared proxy, browser cache or mail-scanner log cannot retain a live session token. - Foxl Code: a cloud run whose machine died mid-task is now settled right away. When a coding agent stopped without reporting back, the watchdog marked the task failed after fifteen minutes but never told the billing side, so the compute that did run was never charged. The watchdog now finalizes the run the same way a normal finish does, and you get the same "task failed" notification.
- Low-severity hardening from the 2026-09-02 audit, Foxl Code server and runtimes. A per-task budget override above the larger of your per-task budget setting and ten times the estimate is refused rather than merely logged; a coding agent can only record a pull request on its own repository; plan documents prove the repository and installation they name; schedule and Work requests write the same cross-tenant audit row every other surface does; repository files served by Foxl Code carry
nosniffand an attachment disposition; the session verifier accepts only the algorithm the relay signs with; two unused Settings fields are validated instead of stored raw; a machine's reported error is bounded on every path; each cron leg reports its own failure; and the cloud runtimes validate the task id and git ref they are handed. (#946, #920, #913, #919, #930, #914, #917, #916, #932, #926, #927, #925, #924, #933, #912, #918) - A sign-in link can now only be finished by the browser that started it. Signing in with Google, Apple or GitHub hands your browser back to Foxl with a one-time code, and Foxl checked that the accompanying ticket was genuine and recent - but not that it belonged to the browser presenting it, and not that it had never been used before. Someone could start a sign-in as themselves, keep the return link instead of following it, and send it to you; opening it would have signed your browser into their account, so anything you then wrote would land where they could read it. Foxl now leaves a short-lived note in your browser when you start signing in, finishes only a return that matches it, and accepts each one once. A return that does not match shows a plain "start sign-in again" page and signs nothing in. The desktop and phone apps already had their own check for this; the web sign-in did not.
- Sign in with Apple now insists on a verified address, and remembers your Apple ID rather than your email. Apple can pass along an address it has not verified, and Foxl accepted it as proof of ownership and matched it to whichever Foxl account held that address - the check Google sign-in has always had was missing here. Apple sign-in is now refused unless Apple says the address is verified, and once you have signed in with an Apple ID, that ID is what finds your account from then on, so changing the email on your Apple ID no longer creates a second Foxl account.
- Foxl Code no longer sends its internal error text to your browser. When something failed inside the Foxl Code service in a way it had not planned for - a database not yet migrated, a GitHub call that failed - the raw internal message, including table and column names and GitHub API addresses, came back as the error you saw. The service now answers with a generic error plus a reference id, and writes the real cause to its own logs under that id so we can find it when you report it. Errors that were written for you - a refused request, a missing machine, a budget floor - read exactly as before.
- The reviewer that can merge a pull request on your approval now leaves the same audit trail as every other Foxl Code agent. Every agent Foxl runs on your behalf records the GitHub-touching tool calls it makes, so "what did it do" can be answered afterwards - except the one that reviews a pull request after you press Approve, which is also the one allowed to merge it. Its tool calls are recorded like the rest now, tagged with the review they belong to.
- The agent now asks before sending a file to a chat, and can only message people you allowed. The
channel_sendtool used to accept any file on your disk and any recipient id, with no approval prompt, so a malicious web page or message could have had the agent send a private key to a stranger's Telegram chat. It now asks for approval the wayexecdoes, refuses any file outside your Foxl workspace, and refuses any recipient that is not in that channel's allowlist on the Channels page. - Your Claude Code or Codex sign-in is only ever copied to your own Foxl account, and only from this computer. When you sync a coding-assistant sign-in to Foxl Code, Foxl reads that credential off this machine and uploads it for you. Until now the request could say which account should receive it, and it could arrive from another device through your desktop connection - so anything that could reach your desktop could have had your sign-in delivered somewhere else. Foxl now uses the account this computer itself is signed in to, refuses any destination named in the request, and only performs the sync when it is asked from Foxl on this computer. If you are signed out here, it tells you instead of sending anything.
- The Feed Generator's watch command runs only if you set it and your tool-approval rules allow it. The optional
watchcommand in HEARTBEAT.md ran on every tick with no approval check, and HEARTBEAT.md is a file the agent itself can write, so a prompt injection could plant a command that ran unattended from the next launch on. Foxl now runs only the command you set through the Feed Generator settings, refuses and logs a command that appeared in the file any other way, and puts it through the same permission rules and approval prompt as any shell command. A run nobody approves is skipped, not executed. - A coding agent can no longer attach someone else's pull request to your task. When an agent reported the PR it had opened, the link it sent was checked for shape only, so a prompt-injected agent could name a pull request in an entirely different repository and it would appear as your task's "View PR" link and in its list of pull requests. The reported link now has to be a pull request of the task's own repository, with a number that can actually exist; anything else is logged and ignored while the task carries on. The task's activity feed and notifications are covered too: they no longer show, or offer to merge, a pull request the agent merely claimed.
- The agent's
web_fetchtool now refuses a public web address that secretly points at your own computer or network. It already refusedlocalhostand private IP addresses by name, but a domain name can resolve to one of those addresses anyway (for example127.0.0.1.nip.io), and a malicious page could use that to make the agent read a service running on your machine and hand the response back to the model. The tool now looks up the address itself, refuses if any answer is local or private, and connects only to the exact address it checked - on every redirect too. - A purchase is credited only to the account that started the checkout. Lemon Squeezy identifies its customers by the email typed into the checkout, which nobody verifies, so someone who paid once with your email could have become the account your later subscription was credited to - you would be charged and stay on the free plan. A payment whose checkout proof names a different account than the one on file is now applied to nobody and queued for a person to look at, and a Lemon Squeezy customer can be bound to only one account. Billing is not switched on yet, so no live purchase was affected.
- A sign-in link now only completes in the desktop app that started it. For twenty minutes after you pressed Sign in, the desktop app used to accept any
foxl://sign-in link fired at it - by a page in your browser or by another program on your computer - and could quietly switch you into someone else's account. Every sign-in the app starts (Google, Apple, GitHub and the email link) now carries a one-time code that the returning link has to match, anything else is refused, and the app tells you when a link was refused so an expired email link is not mistaken for a sign-in that did nothing. - The agent can no longer rewrite its own standing instructions without asking. The workspace memory tools could edit SOUL.md, HEARTBEAT.md and AGENTS.md - the files that shape every conversation and drive the heartbeat timer - with no approval prompt, so a malicious web page or message could have planted a permanent instruction the agent would follow in every future run, including unattended ones. Those three files are now off limits to the agent entirely (you still edit them on the Workspace page), and saving or editing the remaining memory files asks for approval the way writing a file does.
- Two test-only API routes no longer exist on a shipped desktop.
POST /api/test/notificationandPOST /api/test/approvalwere built for the end-to-end suites but were registered in every build, so anything that could reach the local API could show every connected client a made-up notification, or put a tool-approval prompt on your screen for a tool nothing had asked to run - and pressing "Always allow" on that fake prompt installed a real permission rule. Both routes now exist only when the server is started withFOXL_TEST_ROUTES=1, and "Always allow" can only create a rule for a prompt that a real tool call opened, so a fabricated prompt cannot become a permission even on a test server. The "Send test" button in Settings > Notifications still works on a shipped desktop; it now shows the bell entry and the OS notification directly instead of asking the server to broadcast them. - Restoring a skills backup can no longer delete anything outside the skills folder. The undo for "Sync skills" read the list of skills to put back from a small file kept beside the backup, and trusted it: a crafted entry could name any folder on the computer, and that folder was removed before the copy. Every entry is now checked to be a plain skill name and the restore refuses outright if any is not. Each skill is also copied into place beside the old one and swapped in, so a copy that fails halfway leaves the installed skill exactly as it was.
- A stolen sign-in token alone can no longer impersonate your desktop. Connecting to the relay as your desktop now also requires a secret that only your desktop holds: the Foxl app creates it once, keeps it on this machine, and the relay pins the first one it sees. Anyone else presenting just your sign-in token is refused. Desktops on older versions keep working while they update; there is nothing to set up.
- A custom tool can no longer take a built-in tool's name or erase its safety rules. Saving a custom tool called
exec(orExec, orfile-readnext to the built-infile_read) used to silently remove the built-in refusals forrm -rf /,rm -rf ~,mkfsanddd, and then broke every agent run with an opaque "already registered" error. Such a name is now refused when the tool is created or renamed, with a message naming the built-in it collides with, and registering a custom tool's approval rule leaves the built-in refusals untouched. - Your Telegram bot no longer tells strangers it is online. Two commands, /status and /help, answered anyone who found the bot, while every other message already required the sender to be on your allowed list. Someone who guessed the bot's name could learn that a Foxl desktop was connected and reachable, and get their own user id back to quote when asking you to let them in. Both commands now stay silent for anyone not on the list, the same way photos and files from a stranger already did.
- A notification can no longer sign you into someone else's account or hand another app a link. Tapping a notification - or that notification's row in the in-app list - opens the link it carries. The in-app row accepted any link at all, including ones that launch other apps on your phone, and both places kept the link's
#fragment when they navigated inside Foxl. That fragment is exactly where the app looks for sign-in tokens after a reload, so a crafted link could plant someone else's tokens there and your next sign-out would have signed you straight back in as them. Notification links now open only when they are ordinaryhttps://links, a link into the app never carries a fragment, a link that looks like a sign-in token is refused outright, and every place a notification can be opened from goes through that one check. Today every such link is written by Foxl's own servers and nothing has been seen exploiting this; the change is there so that a future server bug cannot turn into an account takeover. - A fault inside the tool-approval system now refuses the tool call instead of running it. If the approval prompt could not be raised - for example because the connection that carries it failed - the agent used to run the command anyway, with a single warning in the server log. The same was true when the permission system failed to attach to an agent at all: it kept its full toolset with no rules and no prompts. Both now fail closed: the call is refused with a readable reason in the conversation, and an agent whose safety hooks could not attach refuses every tool until Foxl is restarted.
- The diagnostics report leaves your conversations out unless you ask. The System Report in Settings, meant for pasting into a support thread, used to include the last ten messages of your most recent conversation and the titles of your ten most recent ones alongside the versions, paths and logs. It now contains diagnostics only. If a conversation is relevant to the problem, a separate "Include recent conversation" action asks you to confirm before it adds that text to the download.
- A misbehaving coding-agent container can no longer fill the database every Foxl Code user shares. A container reporting on its task could post events of any kind and any size, as fast as it liked, and every one was stored twice with nothing ever cleaning up - so a single compromised run could have driven the shared database to its size limit and taken every other user's tasks down with it. Events are now checked against the list of kinds the runtimes actually send, capped in size (64 KB, above the largest real one), limited to 600 a minute and to 5,000 events or 8 MB per task, and the stored events of tasks that finished more than 30 days ago are swept out on a schedule.
- The status page no longer accepts a webhook address that names an internal host with a trailing dot. Subscribing to incident updates on status.foxl.ai is open to anyone, and the page posts every update to whatever address it stores, so the address filter is the whole guard. That filter was a copy of the relay's link-preview filter and had fallen behind it:
https://localhost./andhttps://metadata.google.internal./slipped past the name checks (a trailing dot is the same host to every resolver), and two reserved IPv4 ranges were not refused. The copy now matches the relay's filter, both filters refuse those two ranges, and a CI test fails whenever the two disagree again instead of leaving that to a comment. - Signing in as a different person on a shared browser now removes the previous account's data, as it was always meant to. If your session expired or was rejected and someone else then signed in on the same browser profile, they inherited everything the first account had stored locally: the chat history kept for offline use, notification text, the list of tools the agent had standing permission to run, and any saved API key. The check that was supposed to catch this compared the new sign-in against itself, so it could never fire. The previous owner is now recorded before a new sign-in is stored and compared properly, and every sign-in path goes through the same check. Signing back in as yourself, and the routine token refresh, leave your data untouched; your theme, language and other device preferences are kept in every case.
- Links in replies and documents open only web pages, email, and the few apps Foxl supports. A link in an AI reply, or in a Markdown file from your workspace, could point at almost any program installed on your computer, and the link text did not have to say so. Links now open only web and email addresses plus Obsidian, VS Code, Slack and phone numbers; anything else stays a plain, unclickable link. Hovering a link that would open another app now shows which one.
- A shell command now sees only the integration secrets its own skill declares, and prints none of them. Every stored integration value used to be passed to every shell command the agent ran, whatever skill was in play, so a single
envcommand - which a planted web page could ask for - handed the model every token you had entered. A command now receives a stored value only after the agent has opened the skill that declares it, in that conversation; a command that names a value it did not get is told to open the skill first; and a credential's value is replaced with a marker in the command's output before the agent reads it. - The Logs console now masks connection tokens and provider key shapes. The recent-logs view keeps thousands of lines of raw server output and is readable from your phone, and its masking missed several real shapes: the token in a tunnel URL or a header dump, Anthropic and OpenAI keys, Slack and GitHub tokens, and AWS secret keys. They are masked now, and ordinary log lines are left exactly as they were.
- A saved API key is shown as its last four characters only. Settings used to display a stored provider key as its first four and last four characters, which for a short key is most of the secret, and the same masked value was returned to every connected client. The mask is now a fixed
****1234- enough to tell which key is in use, not enough to recover it - and a key shorter than 16 characters shows nothing at all. Transcription keys pasted into Notes are also checked for a plausible shape before they are stored, and a rejected paste says so instead of saving something no session could use. - The desktop auto-updater is pinned to a version past its published advisory. Foxl Desktop's update client was allowed to float across a range that npm flags for a credential leak on cross-origin redirects. Foxl's update feed is public and sends no credential, so nothing could leak, but the component that decides what you install next now ships as one known, reviewed version rather than whatever the build machine resolved that day.
- Importing another agent's configuration can no longer overwrite your SOUL.md or plant instructions in it. The OpenClaw / Claude Code importer used to write the imported agent's personality text straight into SOUL.md - the file Foxl embodies in every conversation - replacing whatever you had written there. It now leaves an existing SOUL.md untouched and writes the import to SOUL.imported.md for you to review, quotes the imported text as a description rather than as instructions, and only reads a source folder that sits inside your home directory.
- Tool approval rules can ask or deny, and a rule about one tool now beats "auto-approve all". Settings -> Tool approval used to save every rule as "allow", and a hand-written "always ask before exec" still lost to the auto-approve switch. Each rule now carries allow, ask or deny; the more specific rule wins (a rule for
exec(rm *)beats one forexec, which beats the switch), and between equally specific rules deny beats ask beats allow. Auto-approve all works exactly as before for every tool you have not written a rule about. - A skill can no longer switch on a tool that your tool profile excludes. A skill may declare the tools it provides, and until now that declaration overrode the tool profile: connecting or disconnecting an integration such as Outlook re-registered every skill's tools and handed
execback to a run you had narrowed to theminimalprofile. The profile is the upper bound now - a skill can add a tool only where the profile already allows it,minimalstays minimal, and the server log names any tool a skill asked for that the profile refused. - The desktop's connection token no longer lands in log files or in the copied log. The local server announces the token that authorises every local API call on its own output, and both the app and the background service wrote that line into their daily log files - readable by anything running as you, unlike the token's own store - while "Copy log" put it on the clipboard. Every line that is written or copied now masks that token, and any other credential-shaped value, to its first eight characters. The app itself still receives the token exactly as before.
- A symlink inside your workspace can no longer reach files outside it. The workspace checked paths by name only, so a link placed in the workspace folder - by a command the agent ran, an unpacked skill, or a synced folder - could be followed out to the rest of your disk by every workspace action: opening, saving, searching, moving, copying and deleting, including from the phone. Every path is now resolved through the links it contains before it is allowed, using the same check Foxl Code already uses. Links that stay inside the workspace keep working.
- The relay's web framework is updated past a published denial-of-service advisory. The version it ran carried twelve published advisories, one of them a slow-path in the cross-origin preflight handling (GHSA-8j4g-w8fx-2239); the relay's own configuration did not reach that path, and the update clears all twelve.
- A Foxl Code machine you own now honours your "ask before acting" setting. The agent that runs tasks on your own box always launched the coding CLI with permissions skipped, whatever Settings said, so turning approvals on had no effect on the one machine where it matters most - the one with your home directory and your credentials on its disk. The setting is now read the same way it is for cloud tasks, and the terminal says which mode the run was launched in.
- One task on your machine can no longer leave a trap for the next. The small script that hands git its GitHub token during a push was written once, kept inside the folder the coding CLI used as its home, and reused by every task after it - so code from an untrusted repository could edit it and read the token of whatever repository ran next. Each task now gets its own freshly written script and its own home folder, both removed when the task ends.
- A different copy of Foxl can no longer silently take over the background service. With "keep running when closed" on, every launch repaired the background service's file so it kept pointing at the installed app - and any copy of Foxl you happened to open could repoint it at itself without a word. The service now stays with a copy that carries the same identity: on Windows and Linux that is the installed path, so a build you did not install is refused; on macOS it is the signing team and bundle identifier, so a genuine Foxl signed by us is still allowed while anything else is refused. A refusal is written to the log, and turning the mode off and on in Settings is how you move the service on purpose.
- Only the Flow pill can type a dictation into another app, and only the one it just captured. The paste that lands your words in the frontmost application is a synthetic Cmd/Ctrl+V, and until now any part of the app's own interface could ask for it with any text. It is now honoured only from the dictation pill, only within a few minutes of you pressing the hold key or shortcut, once per capture, and only for ordinary text - a request carrying escape or other control characters is refused rather than typed.
- Screen and system-audio capture is granted only while the Notes recorder asked for it. The desktop used to answer any request to capture the screen and system audio with a silent yes and no on-screen indication, and that answer was shared by every window the app opens - so a compromised page could have started an unseen recording. Capture is now allowed only in the moment the Notes recorder starts a recording, and is refused otherwise.
- The app opens files only from your own folders, and never launches a program. Cmd-clicking a path in a reply, or Notes' "Open file", used to hand any path straight to the operating system, which opens a document but also runs a script or an app bundle. Files now open only from your workspace, Documents, Downloads, Desktop and the Notes folders, and anything that would run rather than open - a script, an installer, an app - is refused and the refusal is written to the log. Folders still open anywhere,
~works everywhere, and "Reveal in Finder" still works for any file that exists. - The Foxl app can no longer be used as a plain Node interpreter. Until now another program on your computer could start the signed Foxl app as a bare scripting runtime and run its own code under Foxl's name - with the microphone, screen-recording and other permissions you had granted Foxl - or attach a debugger to it. The packaged app now refuses those switches, and it verifies its own bundled code before it loads it, so a tampered copy stops instead of running.
- Your desktop's own secret is kept in the system keychain, the settings file is no longer world-writable, and turning off "keep running when closed" wipes the sign-in tokens it kept. The secret that identifies this desktop to the tunnel is now sealed with your computer's keychain instead of sitting readable in a settings file, and that file is readable by your account only (it used to be writable by everyone). The background service still needs its sign-in tokens in a private file while it is on - it runs without the app and cannot use the keychain - but the moment you turn the mode off, those tokens are removed rather than left behind.
- A single relay request can no longer spend more credits than your balance holds. When a request was admitted, the relay set aside credits for a reply of at most 16,384 tokens but let the model produce up to its full 128,000-token maximum, so one long reply on a frontier model could bill about 155 credits more than had been set aside - and several requests at once multiplied that into a balance far below zero. The relay now sets aside credits for exactly the reply length it forwards to the model, shortens that length to what your balance can pay for when it is running low, and declines the request outright when even a short reply would not fit. Each plan also has a cap on how many requests it can have running at the same time (2 on Free, 8 on Pro, 32 on Ultra, 64 on Enterprise); a request over the cap gets a clear "too many requests in flight" answer and can simply be retried.
- An SVG or HTML file from your workspace can no longer run as the app. Opening one in the browser or on your phone used to offer an Open button that handed the file to a new tab under the app's own address, so a crafted file could have read your session there. An SVG now previews as a picture only, and for these files the Open button is gone: Download (Share on a phone) is the one way to take the file out. A side effect worth knowing: SVG previews now work in the desktop app too, where they used to show "This file could not be displayed".
- A link inside a Word document can no longer run code in the app. Opening a
.docxfrom your workspace or a Shared folder renders its hyperlinks into the page, and a crafted document could hide a script-running link behind ordinary link text. Links in a rendered document now open only web and email addresses, in a new tab; anything else is made inert. The same document could also carry an embedded web page (an HTML fragment pasted into the file) whose script ran the moment the document was opened, with no click at all; embedded HTML fragments are no longer rendered. - Turning on LAN access no longer switches off the desktop's protection against DNS rebinding. With
PILOT_ALLOW_LAN=1set, the check that refuses requests addressed to a name other than your own machine was skipped entirely, so a malicious web page could reach your desktop's local API by pointing its own domain at your computer. LAN mode now accepts your machine's own name and addresses - and anything you list inPILOT_ALLOWED_HOSTS, for a reverse proxy or a Tailscale name - and refuses everything else, exactly as it does with LAN off. - The optional
PILOT_API_KEYlock now covers every sensitive read. The short list of reads allowed without the key was matched by prefix, so it also covered custom-tool exports (including their API keys and shell commands), raw workspace files and previews of deleted chats. The list is exact now and those reads require the key like everything else. A second defect meant the list never actually applied over HTTP, so with a key set even the health check demanded it; the health check, the For You usage counters and the model catalog are now reachable without the key, as intended. The full Overview dashboard is not one of them: it includes the goal of the current run, so it needs the key like everything else. - Running a custom tool from the Tools page now asks first, and nothing can talk your desktop into starting a program you did not agree to. Your desktop is reachable from your phone and from the web app, so anything that could reach it could ask it to create a shell tool and run it, or to start a command of its choosing - with no prompt shown to you, on your own machine. Three things changed. Pressing Run on a shell or script tool goes through the same approval prompt the agent's own tool calls use, and if nobody answers it the command is refused rather than run. The MCP inspector takes one of your saved tools instead of a command from the request. And the first time a saved MCP tool's program is started - by saving it, by re-checking it, by the inspector, or by using it - Foxl asks you, showing the command it is about to run; you are asked once per tool and it is remembered until you change that tool's command, arguments or environment. A program Foxl does start is now given a minimal environment rather than every API key the app holds, so an MCP server that used to pick up a token, an HTTPS proxy setting or a certificate bundle from your environment will report its own error until you add what it needs in that tool's Env field.
- Your existing MCP tools need one approval each. After this update they show as needing approval instead of connecting on their own, and the agent will not see their tools until then. Open the Tools page, press the re-check button beside each one, and approve the command it shows. This is asked even if you have "auto approve all tools" switched on, because that switch is about tool calls rather than about starting programs; for the Run button on shell and script tools, that switch still applies as it always has.
- The app opens or reveals only files in your own folders, and only web links. A request to open or reveal something on your computer used to go straight to the operating system, so anything that could reach the desktop - including a phone or browser connected through the relay - could ask it to launch a program, run a script or open any kind of link. The desktop now opens a file only from your workspace, skills, Documents, Downloads or Desktop folders and never one that can execute, opens a folder from anywhere except inside an app bundle, and opens a link only when it is a normal web address. Anything else is refused and written to the log with the reason.
- A stored skill value can no longer change
PATH, the shell, or any of the other program-selecting variables listed below, for Foxl's shell commands - even when a skill asks for it. Foxl lets you store environment values for skills, and a shell command receives the ones declared by the skill it is using - which is the point. But a skill could declarePATH, an editor or a proxy in that list, and a value stored under such a name then decided whichgit,nodeorsshevery later command actually ran, or what a shell did before it started, with nothing on any screen showing it. Stored values can still add or change ordinary variables. Refused now, whatever a skill declares: the command search path and the shell itself; anything that loads code into a program before it starts (the dynamic linker, and the Node, Python, Perl, Ruby, Java, Go, Rust and OpenSSL start-up settings); shell start-up files; git's own settings, apart from author and committer identity; editor, pager, password-prompt and proxy helpers; the configuration files of npm, AWS, kubectl, Docker and the GitHub CLI; TLS trust; and the home, temporary and program folders, including their Windows equivalents. Each refusal is written to the log once, with the reason and how to remove the entry. This is a named list rather than a rule about every variable a program might read, so build and package-manager settings a skill declares (a compiler,make, a Rust or Ruby toolchain, a package registry) still reach the command. - A provider's API key is only ever sent to that provider's own servers. The endpoint override in Settings > Provider accepted any web address, and your saved key for that provider followed it on every request. It now accepts only the provider's own hosts, including its regional endpoints, and says which hosts those are when it refuses. An endpoint saved earlier that is not one of those hosts is no longer used; the provider's default applies until you choose a listed host. For vLLM and LM Studio, which legitimately run anywhere, moving the endpoint to a new host forgets the key you saved for the old one - in the running app as well as on disk, so no restart is needed - and you enter the key for the new host deliberately.
- "Always allow" on a shell command now approves the command you saw, not everything that starts the same way. One click on
ls -laused to remember a prefix, so a laterls; <anything>- orlsof- ran without asking. It now remembers the exact first word as a whole word (lswith plain arguments), and if the command you approved contains a;, a pipe, a redirection,$(...)or a line break, that single call is approved and nothing is remembered. A remembered command also never covers a later call that brings its own environment variables - a changed search path or a preloaded library changes whatlsactually runs, and on Windows a variable can carry a second command - so that call asks again, even when it was already waiting behind the one you approved. - The
code_searchandbrowseragent tools no longer pass any text through a shell.code_searchused to hand the directory and file pattern it was asked to search to a shell unquoted, andbrowserdid the same with its whole command line, so a malicious web page, repository file or chat message could have made the agent run arbitrary commands on your machine - with no approval prompt, becausecode_searchwas always allowed andbrowserhad no approval rule at all. Both tools now pass their arguments directly to the program (ripgrep or grep, agent-browser) with nothing in between to interpret them,browserasks for approval the wayexecdoes, and a browser command containing shell punctuation outside quotes is refused with an explanation instead of being run. A code search of a folder that does not exist, or with an invalid pattern, now reports the error instead of answering that nothing matched. On Windows thebrowsertool is unavailable for now: it reports an error instead of running, until a follow-up starts agent-browser there without a shell. - Meeting transcripts and AI summaries on the phone are now stored encrypted. On iPhone and Android, Notes kept your recordings - the full transcript, attendee names and the AI summary - as plain text in the app's own storage, which is included in an unencrypted device backup, so anyone with that backup could read every meeting with no key. They are now encrypted on the device with a key held separately, the same way your chat history already was, and existing notes are converted automatically the first time Notes opens. Signing out removes both the notes and the key from the phone. If a device cannot unlock its saved notes - the key is missing or is not the one that sealed them - the notes list says so instead of looking empty, and Notes refuses to save over them. Nothing is deleted; new notes are not saved on that device until it is resolved, and signing out is what clears the locked notes along with the key.
- The desktop app now attaches only to a background service that proves it is Foxl's. With "keep running when closed" on, the app used to treat whatever was listening on its local port as the Foxl service, load it as the app window and send it the local connection token - so another program on the computer that grabbed the port first could take over the window and receive the credential. The service now publishes a one-time identity in its private state file and the app makes the server prove it holds it before loading anything or sending anything; a server that cannot is refused, the refusal is logged, and the startup diagnostics screen says so instead of showing the impostor. This holds against a program running as another user or inside a sandbox; a program running unsandboxed under your own account can read the same private file, and this is not a defence against that. On Windows, the background service from the previous version cannot hand over on its own, so the first start after this update ends it where it can and starts the current one; if that does not happen, the startup diagnostics screen says to press Restart server or to sign out of Windows and back in.
- A finished Foxl Code task can no longer keep spending. Each cloud task carries a short-lived credential for its model calls. It used to stay usable after the task had wrapped up and was waiting for your review - for up to two hours, up to the task's whole budget - and could still post events into the task. It now stops working the moment the task's run is settled, whichever way that run ended: waiting for review, merged, failed or cancelled. A task whose agent is still working keeps it, including one whose pull request opened before the agent had finished, and resuming or reconnecting a task gives its agent a working credential again.
- The speaker-separation models are checked against a known fingerprint before Notes uses them. Installing the on-device transcription engine downloads two model files from the internet, and until now nothing confirmed that what arrived was the file we expected: a bad network could have swapped it, including by redirecting the download to an unencrypted address. Each file is now compared to a fingerprint recorded in the app before it is saved, a mismatch stops the install with a clear message instead of keeping the file, and the download refuses to leave https at any step.
- Signing out on the phone now removes the previous account's meeting notes and audio. On iPhone and Android, Notes kept its recordings, transcripts, AI summaries and audio in the app's own storage, and signing out only cleared the web-style storage - so the next person to sign in on that phone saw the previous account's meetings, with the audio playable. Signing out (or switching accounts) now deletes them from the device - the delete is handed to the phone before the screen reloads, so it is not lost to the reload
- and the Notes screen forgets them immediately rather than at the next relaunch.
- Live chat over the desktop relay is now described accurately: it travels under TLS and the relay can read it, in both directions. The Desktop Relay and Security pages said only "a chat reply while it streams" sat outside the browser-to-desktop encryption. The prompt you send and the conversation history sent along with it take the same plain HTTP path, so the pages now say so, plainly. Nothing about what is protected changed; what changed is that the documentation no longer understates what is not.
- Blocking someone now hides their messages in threads, mentions and search too, not only in the channel timeline, and it hides yours from them in the same three places.
- Renaming a shared skill or document onto a name that already exists says so (409, "a skill with that name already exists") instead of a generic server error that looked like an outage and failed the same way on retry.
- Internal hardening from the 2026-09-02 audit, no user-visible change: the auth rate limit now applies on staging (#954); the session-revoke prefix is validated before it reaches a LIKE (#953); the "Talk to us" form has its own rate-limit bucket so it cannot deny token refreshes to people behind the same address (#942);
/openai/v1/responsesenforces the same per-minute burst window as/v1/messages(#957); the storage reaper never deletes an object a live file row still references (#963) and collects a purged workspace's files on its next pass instead of a month later (#961); a void-paused Lemon Squeezy subscription no longer keeps paid access (#960); a query-string token or WebSocket ticket that a route does not accept is answered 409 rather than a 401 that would loop the client through token refresh (#948); the link-unfurl filter blocks the IPv4-translated, 6to4 and Teredo IPv6 spellings of private addresses (#966); upstream model-provider error bodies stay in the relay log instead of being echoed to the client (#956). - An issue's description can no longer smuggle instructions to the coding agent. When Foxl Code starts work on a GitHub issue - from the Solve button, when you retry a task that was started from an issue, or automatically when an issue is opened - the issue's text now reaches the agent clearly marked as untrusted input, cut off at 8 KB, and placed after any note of yours rather than before it. An issue written to look like an instruction from you is read as part of the problem to solve, not as something to do, and the same holds for the reviewer that checks the resulting pull request.
- Connecting GitHub to Foxl Code now completes only in the account that started it, and only from the browser that authorized GitHub. Before, the link that "Connect GitHub" opened carried a value naming the account that would receive the connection, and anyone who opened that link and authorized GitHub would have had their own repositories connected to the account that made the link. GitHub authorization now only produces a short-lived, single-use code, handed to the browser that completed it (or, on the phone and in the desktop app, to the Foxl app on that device), and your repositories are connected when your own signed-in session redeems that code. A code redeemed by any other account connects nothing and is discarded, and the account that made a link receives nothing from someone else authorizing it. On the phone and in the desktop app the connect finishes when the page GitHub sends you to returns you to Foxl, automatically or through its "Back to Foxl" link. Foxl apps older than this version cannot finish a GitHub connect against the updated service; that page says so and asks you to update the app first.
- Losing access to a GitHub organization now also pauses your Foxl Code schedules there, and a member Foxl had never re-checked is no longer removed on a single bad read. Foxl learns that someone has left an organization in two ways: GitHub reports the removal the moment it happens, and Foxl re-verifies membership periodically on its own in case a report was missed. Two things were wrong. On the periodic pass, a member whose membership had never been re-checked counted as verified at the dawn of time, so one incomplete answer from GitHub - which pages its member list and is eventually consistent - removed them from the installation and cancelled their running tasks immediately, with none of the grace period everyone else gets. And when a removal did happen, by either route, that person's schedules on the installation stayed switched on, so the cron kept trying to start work against a repository they no longer had access to, failing quietly every day with the schedule still showing as enabled. A never-checked member now gets the same grace window as anyone else before anything is taken away, and a removal - whether GitHub reported it or the periodic re-check found it - pauses that person's schedules on the installation in the same step and records why in the audit log.
- Your local Foxl database is now readable only by your own account. The file that holds your conversations, notes, sign-in session and connected-service credentials was created with the operating system's default permissions, which on Linux lets any other account on the same computer read it. Foxl now makes that file owner-only every time it starts, so an existing install is fixed the first time this version runs. The data folders Foxl creates on a new install are private to you as well; folders that are already on disk are left exactly as they are.
Fixes
- Typing into a cloud terminal could stop working permanently, on every assistant. If you typed anything in the first two minutes of a Remote pane - which is exactly when a machine is still cold-starting and cloning your repository - that keystroke was held for delivery, correctly. What was wrong is everything after it: from that moment the pane could only ever answer "The machine is still starting", for as long as it stayed open. It could not tell you the machine had gone to sleep, it could not show you Reconnect, and it could not report an error, so a terminal that had genuinely stopped looked identical to one that was about to answer. Held typing is now delivered as soon as the assistant is ready even if you never touch the keyboard again, and once the startup window closes a machine that really is not there says so and offers Reconnect.
- Opening a long conversation on the phone is fast again. v0.6.27 encrypted the desktop tunnel end to end, and that quietly made every large response far more expensive to download - a long chat could take several seconds to appear over cellular where it used to be immediate. The cause is that our relay's responses are compressed in transit, and encrypted data cannot be compressed, so switching encryption on did not cost a little overhead: it threw away the compression entirely. The tunnel now compresses your data *before* it encrypts it, which puts the download back where it was and keeps the relay unable to read any of it. Measured on one real 1521 KB conversation: 1536 KB of download became 224 KB, so loading it goes from 1.9s to 0.6s on average LTE and from 4.6s to 1.0s on a weak connection. Nothing to turn on, and it applies to the whole tunnel - opening a chat, browsing files, the terminal - on the phone, in the browser and in the desktop app.
- Sending something large from the phone or the browser to your desktop no longer fails silently. The same v0.6.27 change could not encrypt a payload past a certain size and gave up without reporting anything, so a big enough request - saving a long note, writing a sizeable file - simply never arrived. Fixed, with the size limit removed entirely.
- A recording that could not be transcribed now says so, instead of running to the end with an empty transcript. Amazon Transcribe accepts a connection on the shape of its signature and only checks the credential when audio starts flowing, so a lapsed AWS session produced a live microphone, a "Recording" label, and silence - the service's own explanation was written to a developer console, which a phone and a packaged app do not have. The reason is now shown as it arrives, in Amazon's own words, and the recording stands down instead of running to the end and leaving you with a blank note. The same applies to a drained or rate-limited ElevenLabs key. One thing to know: a recording that stops this way does not keep its audio. That is unchanged from before rather than new, and it is being fixed separately.
- Foxl Notes on your computer refuses to sign a transcription socket with expired AWS credentials, rather than opening one that will be rejected. Failing at the start is what lets a phone fall back to the Foxl relay and keep recording; it also means the message you get names the thing to fix (renew the session) instead of describing a transcription outage.
- The Foxl Relay transcription engine appears again for the accounts that may use it. The option was hidden from everybody, including allowlisted accounts, because the app was reading the permission from a place nothing had written to since Notes was merged into the main app.
- On-device transcription is no longer offered on iPhone, iPad, Android or the web app. It runs the speech engine on an Apple Silicon Mac, so it could never work from those surfaces - the row was greyed out with no explanation, and could even appear selectable while a Mac of yours was awake, in which case picking it quietly did something else. Settings now says where it runs. If a Mac of yours has it selected, that choice is untouched.
Features
- Claude Fable 5.1 is in the Amazon Bedrock model list. If you have connected your own AWS account under Settings, Model & provider, it is now one of the models you can pick there. It is the same class as Fable 5 at the same price per token, with cache reads costing a quarter of what they did - which is most of the bill on long agent runs that re-read the same context. Verified against Bedrock in both US regions before shipping, so it works with no region setting of yours to change. It runs on your AWS bill, not on Foxl credits, so it does not appear on the phone, on the web app, or anywhere a Foxl plan pays for the model.
- Settings, Model & provider now has real controls for a bring-your-own-key provider - five of them, and they apply to all nineteen. Until now the only thing you could set for OpenRouter, DeepSeek, Groq, Z.ai, a local vLLM and the rest was the API key, which left several things unreachable from the app entirely:
- Maximum reply length. Some providers - OpenRouter among them - reserve this whole amount against your balance *before* answering and refuse the request outright when the balance cannot cover it. If you have seen "requires more credits, or fewer max_tokens", this is the setting that fixes it; raise it instead when you want a long report in one turn.
- Endpoint. For a regional host or a local server on a different port. The desktop has been able to do this for a while and there was nowhere to type it.
- Only free models. OpenRouter serves 425 models of which 18 cost nothing. One switch offers just those, everywhere - the model picker, a scheduled run, a per-surface pin - so a key with no credit on it can still be used for real work.
- Which upstream to prefer. A reseller serves one model through several operators; you can now ask for the cheapest, the fastest, or the quickest to start, and separately refuse operators that would ignore what Foxl sent or that train on your data. The section says plainly which of these can leave a model with nowhere to run.
- What the key has spent. One button asks the provider and shows the total, today's figure and whatever allowance is left, so a refusal for lack of credit is something you can see coming rather than discover mid-sentence. Which controls appear is the provider's own answer, not a list inside Foxl - so a provider that has no free tier simply shows no free-models switch.
- Remote access is now encrypted between your own two ends, so the relay carries ciphertext it has no key for. When you reach your desktop from a browser or your phone, the two ends agree on a key directly and the relay in between only passes the bytes along. This covers terminal output and what you type into a terminal, the responses to every request your app makes to your desktop - file contents, workspace listings, settings, tool output - and the addresses of those requests too, so the relay no longer learns which endpoints you touched. Each connected client gets its own key, so one browser tab cannot read another's traffic. Nothing to turn on, and an older phone or desktop keeps working exactly as before until it updates. Three things are not covered yet and the docs list them rather than rounding them off: a chat reply while it is streaming, a file preview's raw bytes, and a notification's title and body - that last one because the relay is what builds the push your phone shows on its lock screen, so it has to read the text.
- Your desktop now has a safety number you can check. It is a short code shown on both ends, derived from a key your desktop keeps. Your browser remembers it the first time it connects and refuses the connection if it ever changes, so someone trying to sit in the middle of your two devices cannot do it unnoticed. If you reinstall the desktop app the code changes and you will be asked to confirm it once.
- On-device transcription tells speakers apart now. When you record with "On device", Foxl Notes labels who said what - Speaker 1, Speaker 2 - the same way the Amazon path does, and still without any audio leaving your Mac. This was listed as the thing the on-device option could not do when it shipped; the obstacle turned out to be packaging rather than capability, so it runs two small extra models (about 33 MB, downloaded by the same Install engine button, no account or licence needed) beside the speech model. Measured on conversations with known speakers: the right number of people on all fourteen test recordings, including four real ones, and 99% of the transcript attributed to the right person - 93% in the hardest case, two men with similar voices talking over each other with no pause between turns. Two things worth knowing: when two voices are genuinely very close it will treat them as one person rather than split one person in two, which is the safer mistake for a meeting record; and the labels are Speaker 1..n, which you can rename in the transcript. If you already installed the engine, Settings will offer Install engine once more to add the speaker models - transcription keeps working in the meantime, just without labels.
Changes
- The "Foxl Relay" transcription engine in Notes is now limited to specific accounts. That engine streams audio to Amazon Transcribe on Foxl's own AWS account, and Foxl runs as a free project funded out of pocket - so it is no longer offered to everyone. The option is hidden in Notes settings for accounts that do not have it, and the other destinations are unchanged: Amazon Transcribe with your own AWS account (the default), on-device transcription on Apple Silicon, and Deepgram / AssemblyAI / OpenAI / ElevenLabs with your own key. If your recording provider was already set to Foxl Relay, Settings now says so and asks you to pick one of those instead.
Fixes
- The desktop app opens. The first build of this version did not: it stopped at launch with
Cannot find module './tunnel-sessions.js'and showed an error dialog instead of a window, on macOS, Windows and Linux alike. One file that the tunnel needs was left out of the packaged app, so nothing but a reinstall could get past it. If you already installed the version that failed, download it again from the same place - an app that cannot start also cannot update itself. - Chat works again on OpenRouter - and on the eighteen other bring-your-own-key vendors that share its plumbing. Picking one of them and sending a message answered a bare
HTTP 500with nothing else on screen. The provider had been selected correctly a second earlier; the send itself was what failed, because the chat request re-applies the provider from the message and those vendors are not stored under their own name internally, so the re-apply refused an id it had just written. Every one of them was affected the same way: DeepSeek, Groq, Z.ai, Kimi, Qwen, Together, Mistral, Fireworks, Perplexity, xAI, Cerebras, SambaNova, AI21, Hugging Face, MiniMax, and a local vLLM or LM Studio server. - The model picker no longer shows a second, unusable OpenAI entry once you add a key for a different vendor. Adding an OpenRouter key made OpenAI's own models appear as their own section, looking exactly like the real thing - and choosing one sent an OpenAI model name to OpenRouter, which does not have it. OpenAI is listed when you have an OpenAI key, and not otherwise.
- A turn on one of those vendors no longer asks for the model's entire output budget. Foxl requested up to 128,000 output tokens for a chat reply, which OpenRouter reserves against your balance up front and refuses outright when the balance cannot cover it (
This request requires more credits, or fewer max_tokens). The number was not even the right model's: it was matched by name against a different provider's catalog, soz-ai/glm-5.2inherited the ceiling of a model on AWS. A turn now asks for a sensible amount, and never more than the vendor says that particular model allows. - Switching from one of those vendors back to a direct provider no longer leaves the previous vendor's endpoint in place. Changing the model from the composer alone could leave the next request pointed at the vendor you had just switched away from, carrying that vendor's key.
- Closed a hole that let any other program on your computer use Foxl's local API without its access token. The desktop app runs a server on your machine for the web and phone apps to reach through the relay, and a secret token is what stops anything else on the same computer from talking to it. That check was skipped when the address was written in capitals, so a request to
/API/...instead of/api/...was answered as if it had the token - which covers the terminal, the tool runner and the list of provider keys. It now checks the address the same way the server routes it, in every letter case, and the token comparison no longer leaks timing information. Nothing about how you use the app changes. - The iPhone and Android apps now refuse a sign-in callback they did not ask for. Foxl opens sign-in in the system browser and comes back through a
foxl://link, and that link could be opened by any web page you visited - so a page could hand the app someone else's session and quietly replace yours, sending the notes, chats and files you made afterwards to their account instead. The app now mints a one-time value before it sends you out to sign in and requires it back on the way in, which is the same check the Mac and Windows apps have always done. Sign-in itself is unchanged: the value survives the phone closing the app while the browser is open, so a normal sign-in still completes when you come back. - A Foxl Code terminal in the sidebar now says which agent is in it, and shows where it runs as an icon. A locally-run agent's row read "This computer" and nothing else, so the one thing worth knowing - that it is a Kiro CLI, or a Claude Code - was the one thing missing, while a cloud row spent its text on "... on Remote". Every row leads with the mark of the agent actually running in it (a local pane resolves that against the CLIs installed on your machine, so "account default" shows what really launched), the label names that agent, and a cloud / laptop / server glyph at the end of the row says where it is. Interactive terminal sessions on the Tasks page are named the same way.
- The floating meeting panel's assistant can answer questions about you again. Ask it "who am I?" and it used to say the transcript does not identify you - true of the transcript, and beside the point on a computer where Foxl Agent already holds your profile, your notes and your past conversations. The panel's ask bar now runs on exactly the same engine the in-window Notes assistant does: your USER.md profile, whatever Settings > Tools has switched on, your custom tools and MCP servers, and a search over earlier meetings and chats. It says which tool it is using while it looks, and lists what it used underneath the answer. Questions asked in the floating panel and in the main window now land on one thread, so each can recall the other. One switch governs both (Notes settings > "Use Foxl Agent tools"); with it off, both go back to reasoning about the transcript alone.
- The Notes assistant now really is the same agent as Foxl Agent, not a lookalike. It was assembled separately from the same parts, which is close enough to look identical and was not: a Notes question got no thinking budget, no prompt caching, none of the automatic model fallback you configured, no retry when the model was briefly overloaded, and a weaker safeguard against a long meeting overflowing the model's context. On a Claude Pro or Max subscription it also sent the wrong tool format, which is the thing that burns subscription quota. All of it now comes from the one place the chat agent is built, so asking Notes "who am I?", "what did I decide about this last month?" or "what does my CV say?" works exactly as it does in chat - your profile, your saved memory, your past meetings and chats, your files. The assistant's own instructions stay short; only the machinery under them changed.
- "Use Foxl Agent tools" in Notes settings no longer shows OFF when it is on. The switch is on by default and always was, but it drew itself as off whenever the AI destination looked unavailable, while every request still sent it as on - so the one place you would check said the opposite of what was happening.
- Turning notifications on in iOS Settings now actually works. If you declined the notification prompt and later switched notifications on for Foxl in the Settings app, the phone never registered for them - so nothing arrived, indefinitely, and Foxl's own Notifications screen still said they were off. Only fully quitting and reopening the app fixed it. Foxl now re-checks when you come back to it. It will not ask you again on its own; coming back from Settings is not a moment you asked to be interrupted.
- The app icon's unread count no longer resets itself when you open Foxl. Opening the app reported "nothing unread" before it had finished reading what had arrived while it was closed, which threw away the real count - so the next notification showed 1 on the icon instead of the number you actually had waiting. Foxl now asks what it is still owed before saying anything, and the count keeps counting.
- A cloud terminal in Foxl Code no longer swallows what you type while its machine is starting. Opening a Remote terminal on Kiro CLI or Claude Code and typing straight away lost the typing: the pane said the machine was still starting and would answer as soon as its terminal was up, and then the terminal came up empty. Codex was unaffected, which is why this read as those two assistants being broken rather than as a typing problem - Codex simply reaches its prompt in about six seconds where the other two take about twenty, and everything typed inside that gap went nowhere. Your keystrokes are now held on the server and delivered, in order, the moment the assistant is actually ready to read them. They survive closing the pane, opening another pane beside it, reloading the page and closing the tab - all four of which used to throw the typing away.
- A cloud terminal that had gone to sleep can be reconnected from the Runtimes screen. It told you to reconnect and offered no way to do it, so a pane left open for more than about fifteen minutes could only be closed - which threw away the checkout the agent had been working in. There is a Reconnect control now, and it resumes on the same workspace.
- A terminal that was wrongly reported as asleep recovers by itself. Once the pane decided a session was gone it never changed its mind, so a single bad answer left a recovery card sitting over an agent that was working fine until the pane was closed and reopened. It now clears as soon as the agent proves otherwise.
- Foxl Code's Work screen ran off the side of the phone, and took the whole page with it. Its five tabs - Needs you, Backlog, Working, Done, Activity - were laid out in a row that could neither shrink nor scroll, so on a 390-point screen the row was 516 points wide and the entire page scrolled sideways: every card below it moved too, and Done and Activity sat off-screen with no way to reach them. The two longest labels also wrapped onto a second line inside a row too short to show it, so "Needs you" was clipped in half. The tabs now scroll sideways on their own, with a soft edge on the right showing there is more to swipe to, and each one is a noticeably bigger target for a thumb. Nothing about the row changes on a desktop.
- The on-device transcription panel no longer appears on machines that cannot run it. It needs an Apple Silicon Mac, and your Notes settings are shared across your devices - so if you chose "On device" on a Mac and then opened Settings on Windows, Linux or your phone, you saw the whole panel: an Install button that could not work and a status line about a Mac you were not looking at. Those machines now see one line saying what it needs, and the greyed-out "On device" option in the picker says why it is greyed out instead of leaving you to guess. Nothing about recording changes; a machine that cannot run it already fell back to the other transcription options.
- On-device transcription in Foxl Notes is about four times faster, and the live line no longer falls behind the conversation. It shipped pointing at the speech model's unquantized build, and the cost of that turned out to be almost entirely in generating the text rather than in listening to the audio: measured on an M5, a 19 second stretch of speech took 4.1 seconds to transcribe, and because the greyed-out live line re-transcribes the whole sentence each time it updates, the finished text arrived up to 4.3 seconds after the speaker had stopped and the live line's updates stretched further and further apart. On the 8-bit build the same clip takes 0.9 seconds, the worst delay drops to 0.42 seconds, and the live line keeps a steady cadence. The transcribed text is unchanged: on clean audio and with noise added, in English and in Korean, the two builds produced character-for-character the same output. The engine also loads twice as fast when you press record, and the one-off download is now about 1.3 GB instead of 2 GB. If you already installed the engine, Settings will offer the install button once more to fetch the smaller model.
Features
- Foxl Notes can transcribe entirely on your Mac, with the audio never leaving it. Recording and transcription in Settings has a third option, "On device", which runs speech recognition on your own Apple Silicon GPU instead of sending the meeting to Amazon. Nothing is uploaded, nothing is metered, and it keeps working with the network off - so a confidential conversation, an interview, or a meeting on a machine with no internet access can still be recorded and read back. It needs a one-off download of about two gigabytes, which you start from that same Settings row and can watch progress for; it lives outside the app, so a Foxl update does not fetch it again. Two things it does NOT do yet, and the setting says both before you pick it: it does not tell speakers apart, so a group meeting is transcribed as one person, and it does not translate the other party live. The AI summary still uses your Foxl account. Choose Amazon Transcribe when you need speaker labels; choose this when you need the audio to stay put.
- The Notes transcript and AI assistant now share one panel on the right, and it can be resized. They used to dock in two different places - the transcript as a fixed narrow column, the AI assistant stacked underneath the editor - so opening both left the notes themselves squeezed from two sides at once, and neither panel could be made any wider. They are tabs in one panel now, the notes surface keeps its full height, and you can drag the panel's edge to whatever width suits the screen. Foxl remembers it. The transcript also gained a talk-time bar at the top when more than one speaker is present, showing how long each of them has held the floor.
Fixes
- Recording on your iPhone with Amazon Transcribe produced no transcript at all, and nothing said why. Notes on a phone signs its own connection to Amazon using a one-hour, transcribe-only pass your computer hands it, so a long meeting keeps going even if the computer goes to sleep. That pass allowed the wrong one of Amazon's two streaming permissions - the connection was accepted, audio streamed for the whole meeting, and Amazon quietly refused to transcribe any of it. It could only ever affect the phone and the web app, which is why the same setting worked on a Mac, and it looked like a broken microphone rather than a permission: recording ran normally to the end and saved an empty transcript. Switching to Foxl Relay was the only way through. Fixed, and the transcription probe now streams real speech through that pass with a control that fails the old way on purpose, so a handshake can never again be mistaken for a working connection.
- A cloud terminal whose agent is still running is no longer told it went to sleep. Foxl asks the machine for the terminal's current screen, and if that one request came back empty it concluded the session had been reaped - showing "This session went to sleep" over an agent that was working, with a Reconnect button that in one pane is not even on screen. It had a grace period for the half-minute a machine takes to start, but the grace was measured from when the task began, so once it ran out the verdict could never change back: a single hiccup twenty minutes in read as gone for as long as the terminal stayed open. Foxl now also asks whether the machine's own output stream is still connected, which stays true for a terminal that is simply idle and waiting, so a momentary failure to read the screen recovers by itself instead of ending the session on screen.
- A speaker you renamed in a Notes transcript gets their own colour again. The colours were keyed on the automatic "Speaker 1", "Speaker 2" labels, so the moment you renamed someone - which is the point of being able to rename them - they and everyone else fell back to the same grey. Any name now gets a stable colour of its own, and the automatic labels keep the colours they always had.
Features
- Your agent can fall back to another model when the one you picked is unavailable, so an overnight run stops dying at a rate limit. A provider that answers "too many requests", "overloaded" or is simply having an outage used to end the run there. Foxl already retried, but retrying asks the *same* model again, which cannot help when that model is the thing that is unavailable - and the runs this hurts most are the ones with nobody watching: a 3am schedule, a Slack mention, something you started from your phone. Settings, Provider now has Automatic fallback: switch it on and list the models to try instead, in order. It only engages after the retries are exhausted, so a healthy primary is always used first and nothing changes for an ordinary turn. The list is drawn from every provider you have set up, not just the current one - which is the point, because when a vendor is down its other models are down too. Off unless you turn it on, since falling back changes which vendor sees the conversation and what the turn costs.
Fixes
- Bringing your own Anthropic API key is much cheaper now. Foxl asks the model to cache the unchanging front of each request - the system prompt and the tool definitions - so the next turn is billed for reading it rather than sending it again. That has always been on for Anthropic models served through AWS Bedrock and through foxl.ai, but the path that uses your own Anthropic key had no way to switch it on until the agent library added one. So every turn re-paid full price for roughly ten thousand tokens of preamble it had already sent. A cached read costs a tenth of that.
- The token and cost figures in the status HUD were wrong on GPT-5.x and Grok, and they are right now - including on the chats you have already had. Providers do not agree about what an "input token" is: Bedrock's Claude models report the cached part of a prompt separately from the input count, while GPT-5.x, Grok and ChatGPT report a single input count that ALREADY contains the cached part. Foxl added them up the same way for both, so on the second family it counted every cached token twice and then billed it at the full input price on top of the cache price. Measured against a real call: $4.21 charged where the truth was $0.38. What you saw was a context bar reading more than 100% of a window the request had comfortably fitted inside (
1.5M / 1.0M) and a cost that could be nine times too high. Both are computed once now, from figures that mean the same thing whichever model answered, and Foxl repairs the history it already stored the first time you open this version - on the machine this was found on that returned 421.7 million phantom tokens and about $2,100 of cost that was never spent. - A chat's usage now includes the subagents it spawned, while its context bar keeps showing only the chat's own fill. That pair is the point of delegating: the work moves off your context window, and until now the window was the only half you could see, because a subagent's tokens were filed under an internal id that no chat ever read. So a fan-out that burned hundreds of thousands of tokens showed up nowhere at all. The HUD's "This chat" panel gains a Delegated line naming those tokens and how many subagents produced them, and the context bar is unchanged by them on purpose - a subagent's prompt is not what is sitting in your window.
- A language that got stuck on an older build now unsticks itself - you no longer have to go and choose Auto detect by hand. v0.6.24 stopped Foxl Code from freezing your language, but it could only stop it happening again: the frozen value was already saved on your device, and an update cannot tell it apart from a language you picked on purpose. So an iPhone switched to English in iOS Settings kept showing Foxl in Korean, with Foxl's own Language row reading 한국어 instead of 자동 감지. This release clears a saved language that nothing recorded you choosing, once, and from now on every choice made in Settings is recorded as yours and is never touched again. If you DID pick a language by hand before this release, Foxl goes back to following your system once and you can pick it again in Settings, General - there is no way to tell the two apart after the fact, and following the system is the setting almost everyone wants. Nothing else about the picker changes.
- Closing a Foxl Code terminal you opened now stops the agent in it. Press Cmd+T on Runtimes and you get a terminal on a cloud machine; until now pressing its X only took the pane off your screen, so the machine kept running and billing with nothing attached to it, and the run stayed in your task list as a live
Terminal - ...row you had to go and stop by hand. Closing one of those cancels it, and the agent still commits and pushes whatever is in the tree on its way out. The X says so before you press it, in the pane and in the sidebar's list of open terminals. A run you are only watching is untouched. Panes opened from "Watch a run", and the one Runtimes opens for you when you arrive, are windows onto a job that was dispatched somewhere else, so closing them keeps saying (and doing) what it always did: the agent keeps running and reopening reattaches. - A Foxl Code terminal on Remote running Codex no longer opens on a question and then ignores you. Codex asks "Do you trust the contents of this directory?" the first time it runs somewhere new, and in the cloud there was nobody to answer it: the pane drew the question, every key you typed went nowhere, and the only clue was a machine that looked like it had started and gone quiet. Foxl now marks the task's own checkout as trusted before Codex starts, exactly as it already did for Codex on your own computer, so the terminal opens straight into its composer.
- Foxl Code tasks that run on Remote no longer stop partway through with nothing to show. Any task that took longer than about fifteen minutes was being shut down mid-work by the cloud runtime, because Foxl was reporting the machine as idle the whole time it was working. Nothing reached you when it happened: the task sat on "running", its terminal stayed empty, and hours later it was quietly marked cancelled. Foxl now tells the runtime it is busy for as long as the agent is actually working, so a task runs to its finish - it writes the code, runs the tests, pushes the branch and opens the pull request - or stops at the time limit you set in Settings and says so. Short tasks were the only ones that ever completed before; they are unaffected.
Features
- On iPhone and iPad, Foxl's language now has its own row in the iOS Settings app, and it follows your system language unless you say otherwise. Settings, Apps, Foxl, Language lists all ten languages Foxl speaks, with the system one marked Default. That is where iOS puts a per-app language and where people look for it first, and until now the row was not there at all, because Foxl had never told iOS which languages it speaks. Nothing changes unless you use it: the picker in Foxl's own Settings still works the same way, and left on Auto detect it follows whatever you choose in iOS.
- Foxl Notes suggests what to say next, in the language the meeting is being held in. Turn on Live reply suggestions (the meeting panel's Options menu, or Settings, Notes) and every time the other person finishes talking a card appears with a line you could actually say: what it does in a few words, the sentence itself, and the same sentence in your own language underneath so a glance is enough. The four buttons under the stack ask for a different angle on the same moment - agree, push back, ask, suggest - and each one adds a card rather than replacing the one you were reading. Nothing disappears on a timer. The cards stack up with the time they arrived, so a suggestion that landed while you were listening is still there when you look up, and you can scroll back to one from earlier in the call. Tap the copy button on a card to put the line on your clipboard. It costs one AI request per turn of the other person, so it starts switched off.
- The suggestion cards have their own text size, and their own window. Card text is a separate slider from the caption size, because a caption is scanned continuously and a card is read in one glance - one slider for both made whichever you did not set it for the wrong size. And "Own window" pops the stack out of the meeting panel into its own floating always-on-top window, so the transcript and the suggestions can sit side by side instead of taking turns behind a tab. It carries the size slider, an always-on-top pin and a clear button, and it keeps mirroring the cards while it is closed, so reopening it is instant and current.
- Foxl Notes transcribes with your own Deepgram, AssemblyAI, OpenAI or ElevenLabs key, not just Amazon Transcribe. Recording had two destinations and both of them were Amazon: the Foxl relay's gateway on Foxl credits, and Amazon Transcribe on your own AWS account. That is a fine default and a bad ceiling - Amazon needs an AWS account and a region before it transcribes a word, and if you already pay Deepgram or OpenAI for speech recognition there was nowhere to say so. Settings, Recording and transcription now lists four more providers alongside the two that were there; pick one, paste its API key, and recordings, imported audio files and Dictate anywhere all go there instead. Your provider bills you directly and no Foxl credit is involved. The key is stored encrypted on your computer and is never sent to Foxl. It is also never handed to the browser: your desktop opens the connection to the provider itself and passes the audio through, which is what the vendors' own documentation asks for - they all say not to put an API key in client-side code. Two consequences worth knowing before you switch. This needs the Foxl desktop app, so on the phone and on app.foxl.ai a recording still runs on Foxl credits (nothing breaks - it falls back on its own and starts recording). And AssemblyAI and OpenAI do not label speakers while streaming, so with either of those the whole transcript is attributed to one speaker; Settings says so under the provider you chose, while Amazon, Deepgram and ElevenLabs all separate voices.
Fixes
- Opening Foxl Code once stopped Foxl from following your system language. The language setting starts on Auto detect, which means "use the system language, and keep using it if I change it later". Visiting Foxl Code quietly froze it to whichever language had been detected at that moment, so changing the language on your phone or your computer afterwards did nothing. If yours is stuck, open Settings, General and choose Auto detect once.
- A Kiro pane no longer stops on a question you cannot get past. Kiro's newer engine asks two things the first time it runs - whether to convert your saved agent configs to its 3.0 format, and whether you really meant to let it use every tool - and it asks the first one before anything else appears. Neither is answered by the flags Foxl passes, so a pane opened Kiro, drew one menu, and sat there: measured at 255 bytes of output in 22 seconds, with the composer never arriving. Foxl now answers both before it launches, and the pane comes up ready to type in about 3 seconds. The configs answer is "not now", so nothing of yours is rewritten - Kiro's own note says a 2.0 config keeps working - and an answer you have already given by hand is left exactly as it is. Cloud Kiro terminals had the same gap and get the same fix.
One thing this does not change: after the pane opens, Kiro spends a while on
Initializingwhile it starts the MCP servers you have configured. That is Kiro's own startup, and how long it takes depends on your servers - on one machine here the slowest took 31 seconds by itself, which is most of the wait. You can type during it; the message is queued. - A cloud Kiro terminal comes up ready to type at, instead of saying "Initializing" forever. Opening a Kiro CLI runtime in Foxl Code drew the welcome screen, printed one line of error text that scrolled away under it, and then sat on
Initializingfor the whole session - so the one thing you could not do with a terminal was type into it. Kiro's new engine claims its session by making a hard link, and the cloud disk your workspace lives on is the one kind of disk that does not support hard links, so the claim could never succeed and start-up never finished. Kiro now keeps its own session files on the machine's local disk, where that works, and comes up at its prompt. Your checkout stays where it was, so nothing about the 14-day workspace changes; Kiro's own chat scrollback no longer survives the machine being recycled, which nothing could read back anyway. Tasks you DISPATCH to Kiro were never affected - they run its older engine, which is why this only ever broke the terminal. - A Kiro task no longer claims to be running a Claude model, or sets aside your Foxl credit to pay for one. Kiro runs on your own Kiro account, and Foxl never tells it which model to use - but it was still recording Claude Opus against the task, so the Runtimes header and the terminal both printed
claude-opus-5under the Kiro mark, and the task reserved credit priced for Opus tokens that Foxl never pays for. On a low balance that reservation could refuse the task outright. A Kiro task is now recorded the same way a Cursor one already was - the vendor picks the model - so it shows no model, reserves only what the machine costs, and is no longer measured against the Claude model list. Nothing about which model Kiro actually uses changes. - Foxl Notes AI works on your own provider key again. If your model came from one of the nineteen OpenAI-compatible providers - OpenRouter, DeepSeek, Groq, Z.ai, Together, Mistral, xAI and the rest - every Notes AI feature refused with "OpenAI API key not configured", while the same model answered perfectly in Agent chat. Notes was asking a part of Foxl that only ever looked for an OpenAI key, so it could not see the provider you had actually chosen. Summaries, auto-titles, coaching reports, the HUD ask and live translation all run on your selected model now. Two things worth knowing if you were hit by this: with an OpenAI key ALSO saved, the refusal became a request to OpenAI carrying the other provider's model name, so if you saw a puzzling OpenAI charge or a 401 from OpenAI while using a different provider, that was this; and a key you paste into Settings now takes effect immediately instead of at the next restart.
- The model picker shows every model your provider actually serves. Foxl shipped a short hand-picked list per provider, and for a reseller like OpenRouter that list was six models out of the 387 it serves - so most of the catalogue was unreachable, and the free models were not offered at all, which left anyone on a free key unable to select a model their key could pay for. Foxl now asks each provider you have configured for its own catalogue and shows the lot, keeping the hand-picked entries and their proper names at the front. Nothing is asked of a provider you have not set up, and a default install is unaffected. This also fixes a related limit: the largest real catalogue was over the size Foxl was willing to read, so it came back empty and looked like a provider serving nothing.
- A Foxl Code cloud terminal now accepts what you type while its machine is still starting - including everything you type after that. A cloud terminal opens straight away and its machine takes about half a minute to come up behind it, so a key pressed in that window cannot be delivered yet. Foxl held that key and retried, which is right, but it held it in the same queue every later keystroke waits in - so the first key typed during the start-up blocked every key pressed after it, for as long as it kept retrying. The terminal looked completely alive the whole time, because output arrives by a different route than typing does. The worst version of this was an agent that opens by asking a question: it drew "Do you trust the contents of this directory?", and then the arrow keys that answer it, and 1, and Enter, all did nothing - a question on screen that could not be answered, on the one surface whose whole job is to accept typing. Keys typed during a start-up are now held together, in the order you typed them, and go out the moment the machine answers.
Features
- Foxl Code keeps your Claude and ChatGPT logins topped up from your computer, so a cloud task stops being refused for a subscription you are signed in to. A cloud coding task runs on your own subscription, and the copy Foxl Code holds is a snapshot of the login sitting on your machine. Foxl Code could renew that snapshot on its own, but only until the next time you used
claudeorcodexyourself, because the vendors hand out a replacement token each time and only one side can hold the current one. From then on the cloud copy was dead, and the next task ended with "your Claude credential has expired and could not be refreshed. Open Foxl Code Settings, Agent credentials, and sync it again from Foxl Desktop" - a chore, raised on a machine that was holding a working login the whole time. Now your computer hands a fresh one over shortly before the cloud copy would run out, on its own. There is a switch for it beside the Sync button in Settings, Agent credentials, and it is on unless you turn it off. It sends only what has gone stale, it never sends anything while the switch is off, and it is absent on code.foxl.ai, which has no computer of yours behind it.
Changes
- Runtimes sits directly under Tasks in the Foxl Code sidebar. The two are the halves of one loop - say what you want, watch it happen - so they are still adjacent with no gap between them; Tasks now leads it.
- Foxl Code's review pane takes comments on a line of the diff, and sends them back to the agent. Click a line number, say what should change, and the footer offers Request changes. Each comment travels with its file, its line and the line itself, so the agent arrives at the right place instead of working it out from prose, and it addresses the feedback on the same branch it already pushed to. Until now the pane could read a diff and merge it: asking for one change meant opening github.com, which is the trip this pane exists to remove. An optional note carries anything about the pull request as a whole. If a pull request has no agent behind it the comments are still posted on GitHub, and it says that rather than claiming something was restarted.
- "needs you" on the Foxl Code home is a link now. The summary line under the greeting reads
2 needs you - 3 working - 97 done, and the first item was the one number you have to act on with no way to act on it - you had to find the Work page yourself. It opens Work underlined on hover, in the same colour it always was, and it stays a link at zero so the control does not appear and disappear as the count moves. - Foxl Code settings now say which subscription unlocks which coding assistant. Agent credentials lists all six assistants with what each one can actually run on - a Claude Pro/Max plan, a ChatGPT Plus/Pro plan, an Anthropic API key, or that vendor's own key - and whether your account is ready for it. The answer used to be spread over three paragraphs in different places, so the commonest wrong conclusion was that one subscription covers all six.
- The coding-agent picker in the Tasks and Terminals headers can open that screen. A row reading "needs your credential" now has somewhere to go: "Subscriptions and keys" takes you straight to the fields. The Terminals menu's old closing line, which named Settings without being able to reach it, is gone.
- The Foxl Code Tasks header is one control now, the same one the Terminals header has:
+ | Remote | Claude Code. It used to be four separate chips saying one sentence with the words scattered - a machine pill, the repo picker, an agent pill and a labelled New-task button. Read left to right it now says what it does: start a task, on that machine, running that agent. The+lost its "New task" label to buy the width back; hovering it still says so.
Fixes
- A Kiro terminal in the cloud no longer opens on a question you cannot answer. Kiro asks you to confirm trust-all-tools mode the first time it runs, with a three-way choice you pick using the arrow keys - and in a cloud terminal that question arrived while the machine was still coming up, so there was nothing to press and the agent never got past it. Every other assistant already had its first-run prompts answered ahead of time; Kiro's one equivalent had been missed, so it happened on every new session. It is answered up front now, and if you had already set the preference yourself, that and the rest of your Kiro settings are kept.
- Typing into a cloud terminal while its machine boots is no longer thrown away. The pane said "The machine is still starting, so that did not reach the agent yet. It will answer as soon as its terminal is up", which was true about the first part and not the second: the keystrokes were dropped, so the terminal came up blank and the only way to find out was to type it again. What you type during the roughly thirty seconds of startup is now held and delivered, in the order you typed it, as soon as the machine answers. A machine that never arrives still stops waiting and offers Reconnect.
- A Foxl Code task that runs on your own computer opens like any other task. With "run coding tasks on this computer" switched on, pressing New task started the agent correctly and then handed you a dead screen:
The live event stream dropped: Error: stream 404. The task existed only on your machine, and every part of the task page was asking the cloud about it. It now has its own page - the live terminal you can type into, the status, the branch, a Stop button, and the pull request link when one lands. - The right-hand rail no longer shows an error about a task that is running fine. It asked the cloud about every selected task, including one running on your own computer, and turned the resulting "no such task" into a red line beside a healthy run.
- A task in the "On this computer" list is pressable while it is still running. A finished one linked to its pull request and a running one did nothing at all, which meant the agent's output had no door: it is the only place that output exists. Every row opens its task now, and the pull request moved into that task's header.
- When Foxl Code cannot reach a backend, the terminal says so and names the fix. It used to report the bare
stream 404, which named neither the host it tried nor anything to change, while every other request on the same screen explained it in full. - Foxl Desktop no longer sends a cloud coding task to the wrong service. On a setup pointed at a relay it cannot map to a Foxl Code host, it asked the relay to start the task and reported the resulting error as a problem with your repository. It now says which setting is missing and starts nothing.
- Files shows your files again - and for the first time, the files a task actually changed. The page had two private sources and could not read either. It picked between them using a setting that describes the CONTAINER's disk, which is the same value for every account, so it only ever asked the one source that was empty - and that source was pointed at a workspace nothing had ever run in, so it was empty by construction too. Measured on the live bucket: a 1003-file checkout was sitting there that nothing in the product would ask for. Files is now four shelves beside Shared, each loaded only when you open it: Tasks (the checkout a coding agent edited, per task), Computer (the same for a task that ran on your own machine, plus the local checkouts chat reads and writes), Storage (your private cloud files), and Session (what the chat brain itself wrote). One shelf being unreachable no longer blanks the others.
- A finished task's files are readable, not just a running one's. The workspace outlives the agent by about two weeks, and just after it finishes is when you want to look - so this surface deliberately has no "is it still running" gate. A workspace that has since expired shows as empty rather than as an error you can do nothing about.
- A file browse can no longer start an agent. The Files page passed its request mode straight through to the runtime, and the runtime dispatches on it, so a value it did not recognise fell through to the path that runs the model. It accepts exactly two read-only modes now.
- A file browse cannot reach anyone else's work. Every path is resolved one segment at a time from the real workspace root, with symlinks followed and checked before the comparison - so a link committed into a cloned repository cannot walk out of it. Which workspace you are reading comes from a task row looked up under your own account, never from anything the request can say.
- A hairline no longer floats in the Terminals header when you have one machine. The machine picker hides itself when there is nothing to switch between, and the divider beside it did not go with it, so the pill drew two dividers around nothing.
- Settings no longer offers Foxl Code settings to an account on the Code waitlist. Foxl Code is in private beta, so it tells you your account is on the waitlist - and Settings went on listing six panes for it (Coding agent, Defaults and budget, Container, Notifications, Machines, This computer), each a form that loaded nothing and could save nothing. The whole Code group is hidden while you are waiting, in Settings and in the Cmd+K search, and it comes back on its own the moment access is granted. Foxl Code itself is untouched: the product is still there and still says where you stand.
- A coding agent opened on this computer now runs the same one your terminal runs. When two copies of a CLI are installed under one name - normal when a wrapper sits alongside the original - the pane could start the other one, because the shell it launches in does not read
~/.zshrcor~/.bashrc, which is where most additions to your path live. Claude Code showed that as a model problem rather than a path one: it read the settings written for the copy you use, could not use the models named there, walked down its fallback list and stopped at "There's an issue with the selected model". The pane now puts the program Foxl itself resolves in front, which is the onewhich claudenames in your terminal, and leaves your path untouched when the two already agree. - A cloud terminal you had just opened said its session had gone to sleep. Open a Foxl Code terminal on Remote, type, and the pane answered "This session went to sleep. Reconnect wakes it on the workspace that was kept." on a session two seconds old, with no Reconnect anywhere on that screen to press. A cloud session gets a fresh machine, which takes about half a minute, and for that whole window the machine answers "I have no session for this" to exactly the same question a machine recycled after fifteen idle minutes answers it to, so the two were indistinguishable and the pane assumed the worse one, permanently, one second into a normal start. The two are now told apart, so a terminal that is merely starting says so ("Starting the agent's machine", and that there is nothing to press), a keystroke typed too early says the machine is not ready yet instead of naming a recovery you do not need, and the recovery card no longer appears over a session that is coming up. Typing during a Reconnect gets the same treatment. A session that really did go to sleep is unaffected: same sentence, same button, and your workspace is still what it resumes on.
- A Foxl Code terminal you open on Remote now starts at the width of the pane it is in. It was always 120 columns wide whatever your window was, so the agent's output wrapped in the wrong place - narrower than the same terminal on this computer, and much wider than a phone - and it stayed wrong for the rest of the session, because the correction was sent while the cloud machine was still starting and there was nothing there to receive it. The width is measured before the session is asked for and sent with the request, so the first thing the agent draws already fits; the terminal also re-sends it the moment the machine answers, so a pane you resize while it starts up catches up too.
- Foxl Code settings can be saved again if you had set an environment variable that Foxl later took over. Saving anything on the Autonomy card or the Settings page failed with "reserved environment variable: CODEX_HOME" - including an autonomy switch that has nothing to do with environment variables. The variable had been fine when it was set; Foxl started setting that one itself afterwards, and every save sends your whole settings row back, so the name Foxl now owns came along and was refused. Such a variable is dropped from your settings on the next save instead of blocking it, which changes nothing about how your tasks run - Foxl has been supplying its own value for it all along. Typing one of the reserved names into the form yourself is still refused, and still tells you which name it was.
- A file path in a reply that starts with
~is clickable for real now, and clicking it opens the file. Cmd-clicking one used to do nothing at all: the underline appeared, the cursor changed, and then silence, with no error anywhere.~is something your shell expands, not something the operating system understands, so~/Downloads/report.pdfwas being looked for inside Foxl's own program folder, where it does not exist. Now the home directory is filled in before the path is handed over, everywhere it can be handed over. - And it opens the file rather than just pointing at it in Finder. A path named at the end of an answer is usually something you want to read, so Cmd-click now opens it in whatever application handles that kind of file. A folder still opens in your file manager, as before.
- A Foxl Code task on Claude Code could not sign in, so it did nothing and then ran out of time. The agent launched, printed "Not logged in - please run /login" to a terminal nobody was looking at, and sat there until its eight-hour ceiling: no edits, no pull request, and a task that looked busy the whole time. Your Claude subscription was fine; one flag on the launch put the CLI into API-key mode, where it never reads the subscription at all. Kiro and Codex tasks were unaffected. Fixed on all three places a task can run: Foxl's cloud, the shared runtime, and a computer of your own.
- A pull request no longer contains Foxl's own instructions file. Every task on a repo without a
CLAUDE.mdorAGENTS.mdadded one, holding our internal agent rules, as a change you were asked to review and merge. The file is still written so the assistant can read it; it just stays out of the commit now. A side effect worth having: a task where the agent did nothing at all now opens no pull request, instead of one containing only that file. - A pull request is titled after what you asked for again. Once a repo had a project brief, the title came out as "Project memory from previous Foxl Code tasks on this repo (may be stale)" - the heading of a note Foxl adds for the agent, not your request.
- The microphone works on Android. Starting a recording in Foxl Notes, or tapping the mic in the composer, was refused outright: the app never got to ask, so the permission dialog never appeared, and the message pointed you at a system setting that had no switch to turn on. Nothing you could do from your phone would have fixed it.
- An iOS reply no longer dies when you switch apps mid-answer. Send a message and leave for another app and the answer was cut off partway, because the app was not asking iOS for the few extra seconds it needed to finish. It asks now, so the reply lands and is waiting for you when you come back.
- Notifications reach the bell again after you sign out and back in on a phone. The app remembered which alerts it had already filed, but forgot the alerts themselves, so anything that arrived before you signed out was skipped forever: live banners on your lock screen and an empty "Nothing here" inside the app. Signing out now clears both, and it also stops a reply you typed from a notification being sent under the next person's account on a shared phone.
- A second phone no longer wipes the number on your first phone's icon. The badge is one number per account, so an Android install kept resetting the count an iPhone was keeping, and the next notification started over at 1 instead of continuing. Each phone's own icon badge still works exactly as before.
Changes
- The Foxl Code Tasks header is one control now, the same one the Terminals header has:
+ | Remote | Claude Code. It used to be four separate chips saying one sentence with the words scattered - a machine pill, the repo picker, an agent pill and a labelled New-task button. Read left to right it now says what it does: start a task, on that machine, running that agent. The+lost its "New task" label to buy the width back; hovering it still says so.
Fixes
- A hairline no longer floats in the Terminals header when you have one machine. The machine picker hides itself when there is nothing to switch between, and the divider beside it did not go with it, so the pill drew two dividers around nothing.
- Settings no longer offers Foxl Code settings to an account on the Code waitlist. Foxl Code is in private beta, so it tells you your account is on the waitlist - and Settings went on listing six panes for it (Coding agent, Defaults and budget, Container, Notifications, Machines, This computer), each a form that loaded nothing and could save nothing. The whole Code group is hidden while you are waiting, in Settings and in the Cmd+K search, and it comes back on its own the moment access is granted. Foxl Code itself is untouched: the product is still there and still says where you stand.
- A coding agent opened on this computer now runs the same one your terminal runs. When two copies of a CLI are installed under one name - normal when a wrapper sits alongside the original - the pane could start the other one, because the shell it launches in does not read
~/.zshrcor~/.bashrc, which is where most additions to your path live. Claude Code showed that as a model problem rather than a path one: it read the settings written for the copy you use, could not use the models named there, walked down its fallback list and stopped at "There's an issue with the selected model". The pane now puts the program Foxl itself resolves in front, which is the onewhich claudenames in your terminal, and leaves your path untouched when the two already agree. - A cloud terminal you had just opened said its session had gone to sleep. Open a Foxl Code terminal on Remote, type, and the pane answered "This session went to sleep. Reconnect wakes it on the workspace that was kept." on a session two seconds old, with no Reconnect anywhere on that screen to press. A cloud session gets a fresh machine, which takes about half a minute, and for that whole window the machine answers "I have no session for this" to exactly the same question a machine recycled after fifteen idle minutes answers it to, so the two were indistinguishable and the pane assumed the worse one, permanently, one second into a normal start. The two are now told apart, so a terminal that is merely starting says so ("Starting the agent's machine", and that there is nothing to press), a keystroke typed too early says the machine is not ready yet instead of naming a recovery you do not need, and the recovery card no longer appears over a session that is coming up. Typing during a Reconnect gets the same treatment. A session that really did go to sleep is unaffected: same sentence, same button, and your workspace is still what it resumes on.
- A Foxl Code terminal you open on Remote now starts at the width of the pane it is in. It was always 120 columns wide whatever your window was, so the agent's output wrapped in the wrong place - narrower than the same terminal on this computer, and much wider than a phone - and it stayed wrong for the rest of the session, because the correction was sent while the cloud machine was still starting and there was nothing there to receive it. The width is measured before the session is asked for and sent with the request, so the first thing the agent draws already fits; the terminal also re-sends it the moment the machine answers, so a pane you resize while it starts up catches up too.
- Foxl Code settings can be saved again if you had set an environment variable that Foxl later took over. Saving anything on the Autonomy card or the Settings page failed with "reserved environment variable: CODEX_HOME" - including an autonomy switch that has nothing to do with environment variables. The variable had been fine when it was set; Foxl started setting that one itself afterwards, and every save sends your whole settings row back, so the name Foxl now owns came along and was refused. Such a variable is dropped from your settings on the next save instead of blocking it, which changes nothing about how your tasks run - Foxl has been supplying its own value for it all along. Typing one of the reserved names into the form yourself is still refused, and still tells you which name it was.
- A file path in a reply that starts with
~is clickable for real now, and clicking it opens the file. Cmd-clicking one used to do nothing at all: the underline appeared, the cursor changed, and then silence, with no error anywhere.~is something your shell expands, not something the operating system understands, so~/Downloads/report.pdfwas being looked for inside Foxl's own program folder, where it does not exist. Now the home directory is filled in before the path is handed over, everywhere it can be handed over. - And it opens the file rather than just pointing at it in Finder. A path named at the end of an answer is usually something you want to read, so Cmd-click now opens it in whatever application handles that kind of file. A folder still opens in your file manager, as before.
- A Foxl Code task on Claude Code could not sign in, so it did nothing and then ran out of time. The agent launched, printed "Not logged in - please run /login" to a terminal nobody was looking at, and sat there until its eight-hour ceiling: no edits, no pull request, and a task that looked busy the whole time. Your Claude subscription was fine; one flag on the launch put the CLI into API-key mode, where it never reads the subscription at all. Kiro and Codex tasks were unaffected. Fixed on all three places a task can run: Foxl's cloud, the shared runtime, and a computer of your own.
- A pull request no longer contains Foxl's own instructions file. Every task on a repo without a
CLAUDE.mdorAGENTS.mdadded one, holding our internal agent rules, as a change you were asked to review and merge. The file is still written so the assistant can read it; it just stays out of the commit now. A side effect worth having: a task where the agent did nothing at all now opens no pull request, instead of one containing only that file. - A pull request is titled after what you asked for again. Once a repo had a project brief, the title came out as "Project memory from previous Foxl Code tasks on this repo (may be stale)" - the heading of a note Foxl adds for the agent, not your request.
Features
- Ask the Notes assistant something and it can now use the same tools Foxl Agent has on your computer. It used to see the transcript and nothing else, so "you have access to my CV and my Obsidian notes, right?" got a no from a product that has been able to read files for a year in the next tab. Now an ask can open your files, search your code and notes, fetch a link, and run a command, using whatever you have switched on in Settings > Tools, custom tools and MCP servers included. It tells you which tools it used under the answer. On by default, and there is a switch in Settings > Notes > AI features. Needs Foxl running on your computer.
- It can also remember what you said in earlier conversations. Your Notes threads are saved where the assistant can search them, so "what did I say about this last week" finds the meeting you said it in, and so does Foxl Agent chat. Meeting threads stay out of your chat list; they are only reachable by search.
- Automatic work is deliberately unchanged and still runs on the fastest path: the post-meeting summary, the auto title, live translation and dictation clean-up use no tools, so nothing you were not waiting for got slower.
- The Tasks header now says which coding agent your next task will run, and lets you change it there. The Runtimes header already carried that dropdown; the screen where tasks are actually started did not, so the choice lived only in Settings and nothing on screen said what it currently was. All six agents are listed, with the ones that need provisioning or a credential of yours marked. It sets your account default, so it also applies to scheduled tasks and to the autonomous loop - never to a task already running, and the menu says so.
- Foxl Code's orchestrator can read GitHub when it runs on your own computer. It could plan and start work but not look at the repository it was planning about: every GitHub read answered "no GitHub token available - connect a repo first", which named the wrong problem, since the repo was connected and the credential was the missing half. It now authenticates the way you already do (
gh auth login, your git credential helper, orFOXL_CODE_GITHUB_TOKEN), and the refusal names the real cause when no credential exists. Reads stay confined to the repository you have selected.
Fixes
- The round buttons in the top corners no longer come up as squares on iPhone and iPad. Occasionally the sidebar button on the left and the buttons on the right rendered as a square, or as a rounded square, instead of a circle - and once it happened it stayed that way until the app was restarted. The buttons are drawn by iOS itself, and their corner rounding was measured only once, when the button first appeared: if that measurement landed before the layout had settled to phone width (or while an iPad window was wide), it captured the wider layout's small corner and then kept it even after the button grew back to its round size. It is re-measured whenever the button changes size now, so it corrects itself instead of waiting for a relaunch.
- The app could come up to a loading screen and stay there after an update, and Restart server could not get it back. If you have "keep running when closed" switched on, the background service that owns the server is reinstalled on every launch - and one step of that reinstall could finish with the old service removed, the new one never registered, and the app told it had succeeded. The app then waited on a server nobody was starting, and the Restart button asked the system to restart a service that no longer existed, which is why it answered instantly and changed nothing. Reinstalling now proves the service is really registered before it reports success, every launch checks for the missing case and repairs it, and Restart server reinstalls the service when it is gone instead of failing. If the service truly cannot be started, Foxl now runs the server itself so the app still opens, and says so in Settings rather than leaving you on a blank screen.
- The server-diagnostics screen also told you the wrong runtime: it printed the app shell's Node version under "runtime" while the server has been running on Bun since v0.6.10. It now names the runtime the server is actually on, says whether the background service is registered, and points at the service's own log instead of showing an empty one.
- Searching your past conversations is fast and ranked now, and it never was. When the agent looks back through what you have said before, it is supposed to use a proper full-text index - the reason the app started shipping a different SQLite build eleven releases ago. That search was written in a way the database refuses, so every single lookup quietly fell back to reading every message you have ever written and comparing text. It still found things, which is why nobody noticed: what it could not do was tell a conversation that is genuinely about your subject from one that mentions it once in passing, and it got slower with every conversation you added. The index is really in use now, the best match comes first, and the excerpt you see is the part that matched.
- A long chat no longer loses its history in the middle of a turn. On a conversation that had grown large - a research session with big tool results, a document being built over an hour - the agent could hit the model's context limit mid-turn, throw away the ENTIRE conversation, and then answer your next message as if it were the first one. It did not say so: the reply arrived looking normal, and the only way to notice was to ask the agent what it could still see. Two things caused it and both are fixed. The conversation is now compressed on the way up - when it reaches 70% of the model's window, the oldest part is summarized and kept - instead of only being dealt with after the model has already refused the request. And the summarizer can now actually run: its "always keep this many recent messages" setting was larger than the number of messages a big conversation typically has, which made summarizing arithmetically impossible for exactly the chats that needed it, so the only remaining escape was to drop everything. Compaction is also written to the log now, with the message count before and after, so a turn that loses context can no longer do it silently.
- A new chat is empty now, including the message box. Cmd+N (and the sidebar +, the Chat row, and File > New) started a fresh conversation but left everything you had been about to send sitting there: the half-written sentence, the images and files you had attached, and the cards for any long text you had pasted. The attachments were the expensive part, since the next message you typed sent them along with it. Pressing it a second time on an already empty screen did nothing at all, so there was no way to clear it from the keyboard. The find bar and the open file tabs from the previous conversation also stayed behind, and both now go with it.
- New task in Foxl Code really does start a new task now. Pressing it left the previous conversation on the screen, and reloading brought back one you thought you had thrown away, because there was only ever ONE draft behind the composer: a single conversation per account that every new task reused, on this device and on the server. So there was no new conversation to hand you. Now a task gets its own identity the moment you type something and press enter, exactly like a new chat in Foxl Agent, and until then the screen holds nothing at all: no transcript, no leftover half-written message in the box, and nothing on the server for a reload to find. Whatever the old shared draft was holding is deleted rather than carried forward, and your existing tasks and their conversations are untouched.
- Pressing a choice Foxl Code offered you now settles it everywhere, instead of leaving the button you just pressed still asking. When the agent stops to ask you something, that ask was drawn TWICE on one screen - once in the chat and once in the "Needs you" rail - and each copy tracked separately whether it had been answered. So answering in the chat left the rail showing the identical question with both buttons live and its badge still counting it, and answering in the rail left the chat's copy standing. Measured on the shipped bundle: two copies, four live buttons, badge unchanged. The answer was really being sent the whole time, which is what made it read as a dead button rather than a failure.
- An answer now sits in the conversation where you gave it, instead of piling up at the bottom. Answered asks were pinned under the transcript rather than placed in it, so they never moved: keep chatting and an exchange from five turns ago was drawn BELOW the newest reply, with up to five of them collecting in a block at the end. An ask that is still open stays where it was, at the tail under the reply that asked it.
- A coding task running on your own computer is finally VISIBLE in Foxl Code. Turning on local tasks worked - clones happened, assistants ran, real pull requests were opened on GitHub - and the Tasks list showed none of it, because it only ever listed tasks from Foxl's cloud. So the one thing you could not do with the feature was see it. Tasks that ran on this machine now appear under On this computer, with the assistant that ran them, the goal, the repository, how long ago, and a link straight to the pull request once there is one. Only where a Foxl desktop is actually reachable, so the group simply is not there on the web when your machine is asleep.
- A coding task on your own computer now shows you the pull request, even when the assistant opened it itself. Every one of the six assistants was asked to leave git alone and most of them do it anyway: three of them commit, push, open a pull request and then put the branch back the way they found it. Foxl was looking in exactly one place for the work, so a task could finish with a real pull request live on GitHub and tell you nothing happened, or open a second pull request beside the assistant's own. Now the task recognises its own work by commit, adopts the pull request that already exists, and says which of the two of you opened it.
- And a task no longer ships an empty branch. If the assistant did its work on a branch it named itself, Foxl was pushing its own branch, which was still sitting where the clone started - GitHub then refused the pull request with "no commits between", and the work stayed on your disk. The commits that get pushed are now the ones that were measured.
- A Foxl Code terminal running Codex was configured with a model id OpenAI does not serve. The pane said
gpt-5.6-soland the container was set toopenai.gpt-5.6-sol, which is the same model spelled the way Amazon Bedrock spells it. On a ChatGPT plan the Codex CLI talks to OpenAI directly, so it was being asked for a model that does not exist there, and it failed mid-run inside the terminal rather than saying so. The same mistake ran the other way on the unified shell runtime, where Codex on Bedrock was handed the short id Bedrock does not accept. Each auth path now gets the spelling it actually uses. - A terminal you opened with no coding assistant in it no longer claims to be running one. A plain shell showed an assistant's mark and a model name beside it, because the task row keeps both for reasons that have nothing to do with what is on your screen.
- The runtime line under a terminal stops inventing facts. It printed
claude-opus-5whenever the real model had not arrived yet, which on a Codex pane put a Claude model under the Codex mark, and it printedautoas though Cursor were a model called "auto". Each of those is now simply absent until there is something true to show. The branch name also appears immediately instead of a few seconds in.
Fixes
- You can now actually turn on running Foxl Code tasks on your own computer. The switch shipped as an environment variable, and on a normal install there is nowhere to put one: the desktop keeps running when you close it, so its server is started by a background service whose configuration the app rewrites every time you open it - anything you added by hand was undone by the next launch. The answer is stored on your machine now, so it survives a restart, an app update and a reboot. It is still off until you say yes, and saying yes has to come from the machine itself rather than from the web or your phone, because it decides where your code runs.
Features
- A terminal on your own computer now opens in the repo you picked, running the agent you picked. The Foxl Code header reads as one sentence - a repository, then
+ | This computer | Claude Code- and until now the middle part was the only one your own machine paid attention to: the agent came from a dropdown underneath the terminal, the folder from a second one beside it, and the repository at the top reached that pane not at all. So you could havefoxl-ai/acme-webselected while the agent ran in your home directory, with nothing on screen disagreeing. All three now mean the same thing on every machine. - Your repos are cloned into
~/workspaces, and a checkout you already have is used as it is. A repo goes in~/workspaces/<owner>/<name>, so two owners of the same name never collide. If that folder is already there - because you cloned it yourself, or worked in it last week - Foxl starts the agent in it and touches nothing: no second clone, and no fetch or pull that could raise a conflict in a working copy you were in the middle of. - Settings -> Code -> This computer is where all of that is configured: the clone folder, the folder a terminal starts in when no repository is selected, and whether a coding agent asks before it edits files and runs commands.
- Foxl Code can run a coding task on your own computer. Point it at a repository and the clone, the coding CLI, the commit, the push and the pull request all happen on your machine, with your own CLI login and your own GitHub credential - nothing about the task reaches Foxl's cloud. Any of the six assistants can drive it (Claude Code, Codex, Kiro, Cursor, Hermes, OpenCode), each launched the way that vendor's CLI actually wants, and a CLI you have not signed into says so in its own words instead of failing as "the task failed". The orchestrator you talk to in Foxl Code is what starts it, and it tells you which machine the work is on. Off until you turn it on -
FOXL_CODE_LOCAL_AGENT=1on the desktop server - because it moves where every Foxl Code task executes, and that is your decision rather than a release's. Your existing tasks keep running in Foxl's cloud exactly as before.
Changes
- The autonomous activity panel is gone from the Foxl Code home. It was a collapsible box above the conversation whose collapsed state was one line about whether the machine had been busy - "Worked 4 times in the last day", or "Quiet for a day" beside the time of the last run - and expanding it printed the loop's own narration of each tick. The home screen is for saying what you want and watching it happen, so a panel reporting that nothing had happened is the one thing there that never helped. What the loop actually produces still has its own surface, derived from the issue ledger rather than from narration, and the ticks themselves are still recorded.
- The strip under a local terminal is gone. It carried an agent list, a folder list, a switch, and a line reading "Runs as you, on this computer. Stops when it sleeps." - four rows of settings under the output of a session none of them could change, on the one screen where the terminal's height is the whole point. The agent and the repository are the header's job now, the folders and the switch are in Settings, and the sentence went because the pane's own title already says "This computer", so did the button that opened it, and so does the sidebar row beside it.
Fixes
- Opening Foxl Code's Runtimes no longer flashes two grey boxes at you. The page drew a pair of large grey placeholder blocks while it waited, which was left over from the read-only card grid this screen replaced - so it promised a shape the page cannot render and a count it never opens (it opens one terminal, not two), directly after the app's own blur-and-mark wait had just finished. There is one wait now, the same mark used everywhere else, and it hands over to the terminals without a second style of loading in between.
- Returning to Runtimes shows your terminals immediately. The saved arrangement of panes was read one frame too late, so every trip back to the page painted the loading state first and replaced it a moment later - a flash, for a workspace that was already known. It is read before the first frame now, so coming back from another screen puts you straight back in your panes, and closing every pane no longer waits on the task list to tell you the workspace is empty.
- Runtimes no longer sits on a loading state forever when you are signed out. With no session there was no request to end the wait, so the placeholder never cleared.
- Foxl Code's model picker offers the models you actually have, and chat runs on them. It listed Opus 5, Fable 5, Opus 4.8, Sonnet 5 and Haiku 4.5 under a "Foxl" heading, and picking any of them answered "foxl.ai is not serving model inference right now" - because foxl.ai serves none, deliberately, and those five names were a hardcoded list the picker fell back to whenever the catalog came back empty. An empty catalog is exactly what "we serve no models" looks like, so the fallback fired on the normal case. The picker now reads the same providers Foxl Agent does - your own AWS keys, an Anthropic or OpenAI key, a Claude or ChatGPT subscription, Ollama or any OpenAI-compatible endpoint on your machine - grouped by whose they are, and a turn on one of them runs on your computer instead of being sent to foxl.ai. With nothing set up it says so, and says where to fix it, rather than offering five models that cannot answer.
- Choosing a model that lives on your machine while that machine is away now says so. It used to be sent to the cloud anyway, which spent credits you did not choose and failed regardless. Foxl says which computer it needs and offers you the other choice.
- A local terminal that could not reach your computer now says so, instead of the pane breaking. It was supposed to show "This computer did not answer" with a Try again button, and that panel could never actually appear: the code returned it one step too early, which React treats as a fault and reports as a broken pane. So the one situation the message exists for - the desktop app not running behind the page you are looking at - was the one where you could not read it.
- Foxl Code no longer runs anyone on a two-year-old model by accident. If you had ever picked a default machine without also saving a model in Settings, your account was pinned to Claude Opus 4.7 - not by you, by the database: the settings table has carried
claude-opus-4-7as a column default since 4.7 was the flagship, so a row created without naming a model got one written for it. Every part of the product then honoured it, which is why opening a plain terminal could come up on Opus 4.7 and the model picker had nothing to show for it (older models are hidden, so there was no name on screen to disagree with). The database no longer chooses a model for anyone, affected accounts are repaired, and the four default model names now live in one file instead of nine places.
Features
- A Foxl Code terminal can now be just a terminal. The agent picker beside the
+in the Code header has a new first choice,Terminal, and it means what it says: the pane opens a login shell on the machine you picked, with no coding assistant launched in it. Your own computer has always been able to do this, so the same keystroke gave you a terminal on one machine and a coding agent on every other one; nowCmd+Tmeans the same thing everywhere. The shell starts in the repository's checkout on that machine, anything you commit is pushed to the session's own branch when you are done, and closing the pane opens no pull request. It needs no model and no API key of yours, because nothing in it calls a model until you ask for one yourself. - The
+ Terminalbutton in the Code header is now just+. The pill reads as one sentence left to right - open a terminal, on that machine, running that - and with the newTerminalchoice above it the old label named the same thing twice while taking the width a repository name needs. Hovering it still says "New terminal", with the shortcut.
Changes
- The Foxl Code sidebar has one gap in it instead of two. Runtimes and Tasks sit together at the top - say what you want, watch it happen - and Work, Repos, Files and Schedules are one block below them. Schedules used to sit alone under a second gap and Work above the first, which put a separator after nearly every pair and made the run of six rows evenly spaced again, with more air in it.
- The free machine Foxl Code runs your work on is called
Remotenow, notFoxl Cloud. Nothing about it changes: same machine, still free, still the default, still not removable. The name does, because Foxl's direction is a machine you run yourself rather than infrastructure rented from us, and a brand on the managed box read as the opposite. To you there is this machine, your own computer, and any box you have added yourself, which is what the list now says. - A Foxl Code task on the Codex agent starts again. Codex keeps its own working state in a handful of small databases, and the cloud machine's shared folder cannot hold the kind of database it uses. Older Codex versions worked around that quietly by going without most of them; the version now shipping does not, so it refused to start and said another copy of Codex was already using its data. There was no other copy - that message is Codex's guess at a locked file, and it sent the first look at this hunting a process that did not exist. Its state now sits on the machine's own disk, where every one of those databases is created properly. The Codex version is pinned too, so the tool cannot change underneath a task with nothing recording that it moved, which is how this arrived in the first place. Two things worth knowing: Codex's own record of a session no longer survives the machine being recycled, which nothing relied on since a Codex task has never offered Reconnect, and your ChatGPT sign-in now lives on a disk that is erased with the machine rather than one kept for two weeks.
- Foxl Code tasks run on a free account again. While Foxl is free there are no paid plans to sell, so the per-plan limits a task started against had nothing behind them and no way past them: the default coding agent was refused outright because the free plan listed no models, and even with that open the pre-flight credit check compared a task's $500 default budget ceiling against a new account's welcome credits and refused every first task regardless of balance. Both are switched off for as long as plans are unsold, in one place, so turning them back on is a single line. Nothing else moved: the plans themselves still describe what each tier includes, the limit on how many tasks run at once still applies, and an account that has actually run out of credits is still stopped before it starts another task.
- A first message on a fresh install now says what to do about it. Nothing had been connected yet, so the reply came back as the raw text a cloud SDK prints when it finds no credentials at all - naming no cause, no setting and nothing to try. It now leads with the one thing that fixes it and where that lives, and still prints the original text underneath so it can be searched or pasted into a report. Two other refusals got the same treatment: a request refused because foxl.ai serves no model inference used to arrive as a bare code that reads like an outage, and a model your account cannot use said only that, with no next step. None of them suggest buying anything, because there is nothing to buy.
- The model list explains itself when it is empty. Opening the picker with no provider connected said "No models match your search." over an untouched search box, while the composer above it still displayed a model name. It now says that no provider is connected, offers to open the right settings pane, and keeps the search message for an actual search that found nothing.
- Foxl Notes says what it still needs before you press record, not after. On a fresh desktop, transcription is signed with your own AWS credentials and the summary runs on whichever model provider you have connected - and with neither in place, recording opened the microphone and then failed with a raw error, while the summary failure was written into the note as its own text. Notes now shows what is missing up front, names each piece, and links straight to the pane that fixes it. It says nothing once both are in place, and it stays quiet when the desktop is simply not running, which already has its own message.
- Two pieces of advice inside Notes that could not work have been replaced. A failed summary suggested signing in to use your Foxl credits, and a missing model suggested signing in or setting a self-hosted model - three remedies across two messages, all of them switched off. Both now point at the model provider setting that actually serves Notes.
- The Foxl Code setup checklist had three steps and needed four. A task cannot start without your own model access, and nothing in the checklist mentioned it, so it was possible to finish every listed step and have the first task refused by a message buried in a tool result. There is now a step for it, before the one it is a prerequisite of, and it checks itself off when a subscription or key is in place.
- That checklist is reachable again after its first step. Installing the GitHub App redirects the tab and returns you to the repository list, and the only route back to the checklist required having no installation - so on the web the later steps could never be seen. It is now offered whenever setup is unfinished, once per visit, and never when you have deliberately gone to write a task.
- The autonomous activity panel stops printing the same timestamp twice. When the loop had done nothing for a day, its collapsed heading read "Quiet for a day - last ran 6d ago" and the row's own right-hand stamp then said "6d ago" immediately after it, so the line ended in the same time repeated back to back. Both came from the one value. The heading now says only that it has been quiet and the stamp keeps saying when the last run was, in the place every other row in the app puts a time.
Fixes
- Closing a Foxl Code terminal now sticks, and arriving no longer fills the screen with terminals you did not open. Opening the Runtimes workspace put up to three terminals on screen by itself, and pressing the X on one only held until you clicked to another page and back. A cloud session deliberately keeps running after its pane closes, so you can reopen it and carry on - but that also made it the newest live run, which is exactly what the page re-opened for you, so a closed terminal came back on its own. A visit now opens at most one terminal (your newest run), every terminal you close stays closed for the rest of the session, and a workspace you emptied stays empty.
- Channels and DMs stop jumping too far up when the phone keyboard opens. The message bar was still paying for the floating tab bar while the keyboard was up - a bar that is faded out and sitting behind the keys at that moment - so the card floated about 124px above the keyboard and the conversation above it was squeezed by exactly that much. The transcript reserved a second bar's worth of empty space under its newest message as well, on a scroller the tab bar cannot reach at all. Both are gone: the card now comes to rest on the keyboard the way the Agent and Foxl Code message bars already did, and the last message sits just above it, so a short DM no longer shows its only line clipped under the header. With the keyboard closed nothing moves except that the card clears the tab bar by the same margin as the other two chats. In a room with only a message or two, that line also stopped sliding up under the header and dropping back while the keyboard opened.
Fixes
- A terminal in the Foxl Code sidebar stops flashing the wrong icon before its agent's mark appears. Every row in the Runtimes list leads with the mark of the coding agent in that pane, and a pane running a plain shell instead shows a
>_. Those two came from the same missing value, so a pane whose agent we simply had not been told about yet was drawn as a shell and then corrected itself a moment later - on every page load, for every agent pane, because a restored layout knows which panes exist one round trip before it knows what is in them. The row now keeps that space empty until the mark is real, so nothing claims to be a plain shell that is not one. The same rows also stop showing a laptop for a terminal that is running in the cloud: which machine a pane is on is known from the start for Foxl Cloud and for this computer, so only your own remote machines wait for their details, and those now show a plain terminal until they arrive rather than a guess. The Runtimes row itself carries a terminal now rather than an ECG trace, which is what it was when the page was a read-only list of machines. - Closing a cloud terminal from the sidebar warns you that the agent keeps running. It already did from the pane's own close button, and the sidebar row said only "Close" - but only because it had mistaken every freshly-restored pane for one on your own computer, where there is nothing left behind to warn about. Both X's now say the same thing, which was the point of saying it at all.
- A Foxl Code task terminal shows the agent's output and nothing else. The pane used to mix in lines we wrote ourselves - one announcing that the agent had started, one per shell command, notes about pull requests and budgets, and bracketed messages when our own connection dropped - all timestamped and coloured like terminal output. A terminal reads as a transcript of what ran, so those were indistinguishable from things the agent had said. It was most visible on a task whose machine had been recycled: the pane held a single line saying the agent had started, so the product looked like it had run an agent that then printed nothing, when in truth nothing had printed at all and the only text on screen was ours. All of it is out of the pane. Task progress lives where you can tell it apart from a program's output - the status pill, the pull-request link, the budget chip and the alert chip in the header - and if a reconnect fails or a keystroke does not reach the agent, that is now said in a strip over the pane that goes away by itself once the condition does.
- A cloud terminal that went to sleep now offers the Reconnect it tells you to press. A Foxl Code task's machine is recycled after fifteen idle minutes, and the next thing you type into its terminal is what discovers that. The pane said "this session went to sleep - use Reconnect to wake it on its saved workspace" and then showed no Reconnect, because the only thing that put that button on a task still marked Running was a check that runs for the first eighteen seconds after the pane opens and never again. So a terminal you had been using for a while could only ever print the sentence. Typing again printed it again: one line per keystroke batch, reported as twenty identical red lines and no way out. The keystroke now asks the machine whether it is really gone, and that answer is what raises the button - the same read-only check as before, just no longer limited to the moment you opened the pane. The message is one strip over the pane that says it once and clears itself when the session answers again, instead of a red line per keystroke batch, and your work is not affected either way: Reconnect resumes on the same saved workspace.
- "Which agent" moved out from under the terminal and into the button that opens one. A cloud pane carried a strip reading "New cloud panes run / Claude Code (account default)" with "Keeps going when you close your laptop." beneath it. Neither line was about the pane it sat under: the pick governs the NEXT terminal, and a pane cannot change what the agent already running in it is - so with four panes open you saw four copies of one setting, under output none of them applied to. It is the third segment of the header button now, which reads left to right as what it does: "+ Terminal | Foxl Cloud | Claude Code". The agents that need a key of yours are still marked, in the same words Settings uses. The durability promise is gone from the footer; the pane's close button already says it at the only moment it changes anything ("the agent keeps running"). Every cloud pane gets a row of height back, which on a terminal is the whole point.
Features
- Tell Foxl Code which folder to work in. A coding agent started on "This computer" used to open in your home directory, so the first thing you had to do was
cdto the project - and an agent outside your code cannot read it, run the tests or open a branch. Add a folder in the strip under the terminal (a native picker on the desktop, a typed path from a browser) and new panes start there. The list remembers what you use most and marks any entry whose folder has gone missing. The folder is only a starting point: branches and worktrees stay the agent's own business, and if the folder you picked has gone away Foxl says so and starts nothing rather than quietly running somewhere else.
Fixes
- One type treatment across the Foxl Code header, and "+ Terminal" now names the machine it opens on. The Runtimes bar carried three sizes and two text colours side by side - the page title and the repo picker at 14px dimmed, the machine chip and the New-terminal label at 12px full black, which reads as bolder as well as smaller. They all match now, and New task on Tasks moved with them since it is the same control in another slot. The machine chip is also no longer a separate control on Runtimes: it is the right half of the New-terminal button, because on that page the pick is exactly where the next terminal opens.
- Four more places on the Runtimes workspace that said the wrong thing. The machine button claimed "terminals and tasks" even when the pick was This computer, where a Foxl task cannot run at all - it now says so, and names where tasks go instead. A local pane and a cloud pane in the same split both had a footer reading "New panes run", for two different settings with two different lists; they now say "local" and "cloud", which is what their screen-reader labels always said. Closing a terminal from the SIDEBAR is the same act as closing it from the pane, so it now carries the pane's own warning that a cloud agent keeps running. And the workspace menu's closing paragraph is one line instead of three - it described a layout you are looking at; the part that survives is the 10-second refresh interval, which nothing else on screen can tell you.
- Korean in the Terminal no longer loses every syllable but the last. v0.6.15 stopped the in-progress jamo from reaching the shell, and took the finished syllables with it: typing
한글입력put력on the command line. xterm hands over a completed syllable one task after the composition closes, and a Korean keyboard opens the next syllable's composition in the same keystroke - so the guard was still shut when the finished text arrived. The terminal now holds back the in-progress jamo itself, rather than everything that arrives while the keyboard is composing, so a finished syllable cannot be caught by it - and neither can a key you pressed mid-composition, which the old guard also threw away. Both terminals (Foxl Agent and Foxl Code). - Sign-in, chat, Notes and Foxl Code could all fail to save at once, and the cause was a log nobody reads. Every GitHub webhook the Foxl Code integration received was being stored whole - the entire message body, kept forever - and continuous-integration events alone were adding tens of megabytes a day. That filled the shared database to its ceiling, and once full it rejects EVERY write in the service, not just the one that filled it. Foxl now records that a delivery arrived without keeping a copy of its contents (GitHub keeps those, and can replay them), and old records are cleaned up automatically instead of accumulating. The stored data was 398 MB of a 500 MB limit; it is a few megabytes now.
- Foxl Code now ignores GitHub events it has no use for, instead of filing them. Nearly two thirds of what GitHub was sending - a notification for every individual CI job, every push, and every comment on an issue or a pull request - reached no feature at all, and each one still cost two database writes. Those are answered and dropped immediately now, and the subscription itself has been trimmed to the six events Foxl acts on. One consequence worth stating plainly: ordinary comment activity on your issues and pull requests is no longer sent to Foxl at all.
- The Hermes mark was invisible in Settings in dark mode. Its artwork is a black silhouette that ships with a light and a dark file, and the assistant picker was reading the light one directly instead of asking for the right variant - so on the dark UI it was a black logo on a near-black panel.
- Foxl Code's terminal starts the assistant instead of sitting there. On the shared shell runtime the CLI was launched in a terminal whose home directory was not the workspace, so it could see none of the files Foxl writes for it before a run: the ones that skip onboarding and the trust prompt, your assistant settings, your instructions, and - if you sign in with a Claude subscription - your credential, which was sitting one directory away unused. The pane came up and stopped on a prompt with nothing saying why. Measured against the live runtime rather than inferred: a fresh terminal there reported
HOME=/home/nodewhile the container itself runs on/mnt/workspace. The terminal now takes both the home directory and the image's program search path from the container, so the assistant starts, a reconnect finds its own transcripts again, and the subscription credential is cleared from the workspace at the end of a run the way it was always meant to be. - An agent pane works again if your shell is fish. v0.6.15 taught the pane to repair PATH before starting a coding agent, and it wrote that repair in POSIX shell - so on fish (or nushell) the pane answered with a syntax error instead of starting the agent, on a launch that had worked before. The repair is now only used with shells that speak it.
- On Windows the agent list no longer offers an agent it cannot start. A file named exactly
claude, with no.cmdor.exe, counted as an installed agent - which PowerShell cannot run, so the pane opened and the launch failed with "is not recognized". - Dragging a file onto the agent no longer acts when you change your mind. Letting a workspace row go over the files panel it came from inserted a reference anyway, so a drag of a few pixels did something. The panel refuses the drop now, and the cursor says so before you release.
- A file dragged in while reading a subagent's transcript is no longer attached invisibly. That view has no message box, so the file quietly went into the draft of the conversation behind it and turned up later out of nowhere. It is not a drop target at all now.
- Dragging selected text into the composer works again. The chat page cancelled every drop it did not recognise, so dragging a few words in from anywhere inserted nothing at all. Text dropped on the composer is inserted by the browser now; a link dropped anywhere else is still refused, because the browser's default there is to navigate away from the app.
- Foxl Code: the backlog says which repository it is showing. With one repository on the account the list resolves without the filter chip, and no row carries a repo name, so the only per-repo list in the product did not name it.
- The floating Notes panel's Live tab keeps the whole meeting. It was fed only the last 80 lines, so in anything longer the beginning of the meeting quietly went missing - scroll up in the panel and the transcript just stopped, as if the top had been cut off. It now mirrors the entire transcript, the same thing the main window's transcript panel has always shown, so you can scroll back to the first thing anyone said.
Changes
- Foxl Code's GitHub webhook documentation now matches what it actually subscribes to. The design notes had claimed CI events were not subscribed months after they were, which is the kind of stale note that gets a working feature reported as broken.
- Closing Foxl and opening it again gives you a fresh terminal, not the last one's ghost. Quitting the app was supposed to end every terminal it had open, and it did not: the shutdown asked for the terminal manager and then exited without waiting for the answer, so nothing was closed. Every session stayed on disk and came back on the next launch as a live-looking tab, replaying its old output - including the
exityou had typed - and pressing Cmd+T reattached to one of those instead of opening a new shell, because the app counted the ghosts as terminals you already had. Two more leaks fed the same pile: a terminal you had finished with kept its saved output after the app stopped tracking it, and closing a terminal wrote its scrollback back out a moment later, one orphaned file per close. The old files are swept on the next launch, so the count does not keep growing on installs that were already carrying them. On macOS this could also leave the previous server holding its port, which is the other half of "reopening does not feel stable". - Error messages appear again, everywhere in the app. Every pop-up message Foxl tried to show you - a save that failed, a voice error, a file operation that did not work, 94 of them across 15 screens - was being sent to a message tray that was not the one on screen. The app and its shared component library had each ended up with their own copy of the toast library, and a copy keeps its own queue, so the message was queued in one and the visible tray listened to the other. Nothing failed and nothing was logged; the message simply never arrived. There is now one copy, checked by a gate so it cannot split again.
- Pressing + for a new terminal tells you when the desktop says no. It could refuse (a concurrent-terminal limit, or the terminal component failing to load) and the button simply did nothing at all - the refusal was thrown away. It now shows the desktop's own reason. The + button also stopped going grey at five terminals: the limit had moved to twelve and the button had not, so it disabled itself while the desktop was happy to open more.
- A finished terminal is no longer handed to you as a live one. Splitting the terminal view, or reopening it while one session had ended, could put you in front of the dead session while the working one sat a tab away - it showed old output and then quietly ignored everything you typed. Split now only offers live sessions, and nothing ever defaults to a finished one. Selecting a finished tab yourself still shows its last output, which is the reason those tabs stay.
- Closing a terminal can no longer signal an unrelated program. The force-kill that follows a close five seconds later was aimed at a stored process number with no check that the process was still the shell it belonged to, and the operating system reuses those numbers. It now fires only if the shell really has not exited.
- A browser extension can no longer be routed into a terminal session. The extension connection is deliberately exempt from the desktop's connection token, and the terminal route matched anywhere in the address rather than the path, so a connection to the extension endpoint that mentioned the terminal path in its query was served by the terminal. Both are matched on the path now. Also hardened: a terminal id from a URL could reach outside its own folder when reading saved output.
- Foxl Code: an open terminal in the sidebar now leads with the agent's mark, the way a task row does. These rows carried a bare cloud, laptop or server, which answers "which machine" - a fact the row's own label already states ("This computer", "Foxl Cloud", or the box's name) and the pane header states again. Every other mark in that column identifies who is working, so the one row leading with a piece of infrastructure read as a different kind of thing. A terminal with an agent in it now shows that agent's icon and its status dot; a terminal running a plain shell shows the machine drawn INSIDE a terminal frame, which is also what a pane header shows now rather than picking one of the two.
- The "New terminal" row is gone from that sidebar list. It was never on screen without the + Terminal button beside it in the header, so it was a duplicate of one action sitting inside the list of what is open, which makes the count wrong at a glance.
- Settings: the default coding assistant is a dropdown, like the model provider. It was a full-width card per assistant - mark, name, description and a status chip - and at six assistants that is 477px of the pane spent on one field, measured, which pushed the model and budget controls below it off the screen. It is one 38px row now, with the assistant's mark and any warning shown on the closed control, the same shape the model-provider picker has always had. The selected assistant's description sits under it, and "Not provisioned" and "Needs your credential" still read differently, because only one of them is something you can act on.
- Every Settings row that names a coding assistant now shows its mark, not just the picker: the Codex model row, the Kiro and Cursor key fields, and the two Agent-credentials rows - whose labels name a subscription ("Claude Pro/Max", "ChatGPT Plus/Pro") rather than the assistant, so they were the hardest to find and are exactly where a "Needs your credential" warning sends you.
Security
- A tool you told Foxl to ask about now actually asks. If you turned "auto approve all tools" off, the approval prompt appeared for
execand the Terminal and for nothing else - three kinds of tool were listed as needing your approval, were shown to you as needing it, and then ran without asking.file_write, which takes any path and any content, was one of them: enough to replace a shell profile or a git hook, so "nothing runs a shell without my say-so" was not true. So was every custom tool you wrote - a custom tool wrapping a shell command, or one evaluating JavaScript, ran unattended even though v0.6.10's note said it would ask. And so was any rule you set yourself in Settings > Tool approval for anything outside a short built-in list: that screen offered a choice with no effect. The cause was two separate lists that both had to name a tool before a prompt could appear, and the second one silently allowed anything it had not heard of. It now asks about anything your rules say to ask about. Nothing changes if you leave auto-approve on, which is still the default; and the tools that deliberately never prompt (reading a terminal's output, polling a background command, MCP servers) still do not, because a prompt on every status check is how people learn to click through them. - A machine you own can no longer point Foxl at an address it should not reach. A Foxl Code machine reports its own address on every heartbeat, and Foxl sends that address a task carrying a GitHub token and, on your own-key setups, your model credential. The check that refuses a private or cloud-metadata address missed five ways of writing one - a trailing dot (
localhost.reads aslocalhostto every name server), four ways of hiding an IPv4 address inside an IPv6 one, and the whole100.64.x.xrange, which is what Tailscale uses, so it was the likeliest one to matter for the "point Foxl at a server you already have" setup added in v0.6.14. All of them are refused now, with the reason shown beside the machine, and ordinary public addresses are unaffected.
Features
- Hermes is selectable in Foxl Code. It had been greyed out as "Not provisioned" since the backend was added, because its CLI was in no container image we ship - the vendor's npm installer drags a browser stack that pushed the shared coding-agent image 47 MB past a hard AWS limit, so the whole image could not be deployed. It installs from PyPI now instead, with no browser tooling, in a two-stage build that leaves the build-only pieces behind: the image is 1277 MB against the 2048 MB ceiling, and all six assistants are in it. Hermes runs on your own Anthropic API key (a Claude subscription cannot work there - the two are different credentials on the wire), so it now asks for that key instead of showing you a switch you could not turn on.
- Drag a workspace file straight into the agent's prompt. The files panel beside the chat lists what the agent just wrote, and until now the only way to point the next turn at one of them was to type its path. Drag a row onto the composer instead: an image is attached so the model can see it, and anything else lands as a clickable file reference at your cursor. Dropping a file from Finder still attaches it as before.
- Foxl Code: browse your repository's open issues and pull requests in-app. A new Backlog tab on Work shows what is open on GitHub with nothing running on it yet, and the buttons that were already there - have an agent solve it, decline it, read the diff, merge it - work from that list. Fetched when you open the tab, so it costs nothing on every other visit.
Changes
- The Runtimes workspace has room to breathe. Each pane's title, its model and spend line, and the agent's own output all sat 8 pixels from the sidebar, and the terminal text had no margin at all, so a log line started further left than the title above it. A pane's chrome and its output share one gutter now, so the column reads as a single edge instead of three.
- The machine dropdown is gone from inside a terminal pane. Every pane carried a "Foxl Cloud / This computer" picker above the terminal, which looked like the machine that pane was on and was not: a pane runs one session, so switching it mid-run did not move your agent anywhere - it replaced what the pane was showing and left the agent running, billed, attached to nothing. It also printed a machine name the pane's own title prints one line above it. The pane now shows its machine as an icon beside that title - a cloud, a laptop or a server - and where the next terminal opens is the machine picker in the header, which is what that control was always for.
- The footer under a terminal reads as a control now. "New panes run Claude Code (account default)" and "Keeps going when you close your laptop" were both faint grey text with nothing separating them, so the one you can click looked exactly like the one you cannot. The agent picker is a proper field with a chevron on its own footer bar, and the promise underneath stays plain prose. Same treatment on a "This computer" pane, including its acts-without-asking switch.
- Foxl Code's Runtimes is one clean line again. The terminal workspace had grown a toolbar of its own - the pane count, "+ Terminal Cmd+T", a "Watch a run" picker, Equalize and Zoom - stacked directly under the header, on the one screen where height is the whole point. It is gone. Most of it was already somewhere better: the count is the sidebar's list of open terminals, "New terminal" is a row in that list, and Zoom is a button on each pane's own header. New terminal now sits in the header beside the repo and machine pickers, exactly where Tasks keeps New task, and the two verbs that had no other home - Watch a run, Equalize - are in the small menu next to it, along with the keyboard shortcuts.
- "Needs you" is shared by every Foxl Code screen. The right-hand queue of decisions waiting on you - open questions, plans parked for approval, merge-ready and CI-red pull requests - used to appear only on Tasks. It is on Runtimes, Work, Repos, Files and Schedules now, with the same header button opening and closing it everywhere, so you can see what is waiting without navigating away to find out.
Fixes
- Terminal: Korean, Japanese, Chinese and other non-ASCII filenames printed as question marks (
lsshowed???????? ?????? (2026??? 7???).pdf). The server now starts with a UTF-8 locale even when the OS gives it none, which is what a Dock-launched app gets. The same fix applies to the agent's own shell commands, so file paths with non-ASCII names are readable there too. - A coding agent that runs in your terminal now runs in the pane too. Starting Claude Code, Codex, Kiro or any other agent in a Runtimes pane could fail with a Node version error while typing the same command at the prompt just below it worked - the pane ran the agent in a non-interactive shell, which never reads
~/.zshrc(or~/.bashrc), so nvm, fnm, volta, asdf and mise were all invisible to it and the agent was launched by whatever oldernodehappened to be found first. The pane now looks in those install directories as well, and puts a version manager'snodein front when it is newer than the one the shell found. - An agent installed only through a version manager is offered again. The menu of agents was built from PATH alone, which a desktop launched from the Dock may never have been given (and in "keep running when closed" mode is whatever it was at install time), so an nvm-installed
clauderead as not installed at all. - A Foxl Code terminal connects, instead of explaining why it will not. Two refusals are gone. Pressing Reconnect while the agent was still working printed
reconnect failed - its agent is already attachedin red and then made things worse: the live view was torn down and every new one closed on arrival, so a healthy task became an unreachable terminal. It attaches to the agent that is there now, anything you typed goes to it, and the red line no longer appears - including on tasks that already have one in their history. And a session whose cloud workspace had been cleared (a Foxl Code update, or 14 idle days) used to offer only a paid Retry on a brand-new task; it now rebuilds the workspace from your branch and opens the terminal there, saying so in the pane, since pushed work survives and only the uncommitted tail was lost. - The Foxl Code terminal is black again, in light theme too. It had started following the app's colour theme, which made a cloud task's terminal near-white in light mode while every other terminal in Foxl stayed black - Foxl Agent's, and a Foxl Code pane running an agent on your own machine. A split with one of each in it looked like a rendering fault. One terminal surface now, the same in both themes and the same in both products. The panel around it still follows the theme, so its header stays readable in light mode.
- A terminal on this computer no longer prints everything twice. Opening a Cmd+T pane on Runtimes replayed the session's history into it twice, one copy directly under the other, so a reattached agent looked like it had run its banner and its last answer two times. The history had two sources that both wrote into the same buffer, and now it has one. The Foxl Agent terminal drawer could do the same thing on reopen and is fixed with it.
- The Cmd+T terminal now uses the same type size as the Foxl Agent terminal. It was a size larger than both the Agent drawer and the cloud pane it splits beside, so one workspace showed two type sizes for the same kind of surface.
- Open a second local terminal and the first one stopped taking input. Every terminal but the newest went dead: its output stopped arriving, it never re-fitted, it did not even say it had disconnected - and it still forwarded your keystrokes, so the agent received what you typed and you saw none of it. One line caused it (the panel's double-mount guard was shared by every panel on the page instead of belonging to one), and it made the whole split workspace unusable: two Runtimes panes, a split in the Terminal drawer, or a Code pane open beside the drawer were all enough. Sometimes the older pane never painted at all, which looked exactly like a terminal that had crashed.
- A terminal you closed in Foxl Code is now closed everywhere. Pressing X removed the pane and left the shell running: it stayed in the Agent product's terminal list (both products share one pool), the two surfaces disagreed about what was live, and the next pane you opened ADOPTED it - so a terminal you had closed came back with its scrollback. X now ends the session, its process and its saved scrollback. Navigating away from Runtimes still leaves your agent running and still reattaches when you come back, which is the case that distinction exists for.
- A terminal whose connection dropped no longer sits there unable to be typed into. A server restart, an update or a sleeping laptop closed the socket, and the pane printed one "[Disconnected]" line and then swallowed everything - the only way back was to switch away from the window and return. It now redials by itself (five tries, backing off), which is safe because the shell itself lives on the desktop and outlives the connection. A process that really exited is not chased.
- Typing Korean in a terminal now puts Korean on the line. Hangul arrived one jamo at a time -
ㅊㅇㅈㄹㅇwhere whole syllables were typed - because the terminal sent each keystroke to the shell while the input method was still building the syllable, so the half-finished letter went first and the finished one followed. It waits for the syllable now. Both terminals are fixed (Foxl Agent's and Foxl Code's), and the same applies to Japanese and Chinese, which compose the same way. This was always happening; until the locale fix above it showed up as question marks rather than as the wrong Korean. - The bottom tab bar no longer sits on top of a Foxl Code terminal's command box on a phone. It hides while the terminal is open, which is what Foxl Agent's terminal page has always done - the tab bar is drawn by iOS above the whole page, so nothing in the page could get over it.
- A closed terminal said "[Process exited with code null]". It says "[Terminal closed]" now, and closing a session from one surface is reported to every other surface showing it instead of leaving a live-looking pane that silently drops keystrokes.
- The Runtimes list in the sidebar now matches the Tasks list. Its rows were indented further than everything else in that column - a terminal's name started eight pixels right of every nav label, every chat title and every task title - so the one list that looked custom was that one. Same row spec now: same indent, same icon size, same type.
- Only one of the Tasks / Runtimes lists is open at a time. Both expanded at once put two scrolling lists in the Code section and pushed Repos and Files off the screen. The list under the row you are on opens; the other closes. Opening a terminal also stops highlighting two rows at once.
- Foxl Code: new terminals split the same way every time. Cmd+T used to pick the direction from the shape of the pane you were in, so repeated splits alternated between side-by-side and top-and-bottom and you could not tell where the next terminal would appear. It now follows one axis, shown by a button in the header; Cmd+D and Cmd+Shift+D split right and down as before and each one also sets the axis for later.
Features
- Cmd+T runs a coding agent on this computer. "This computer" was a row in the machine list that said "Coming soon" and could not be picked. It is selectable now, and choosing it in the header dropdown means Cmd+T on Runtimes starts a coding agent on your own Mac - Claude Code, Codex, Kiro, Cursor, Copilot, Gemini, Grok, Droid, Amp, Aider, Goose, OpenCode, Charm, Cline, Kilocode, Codebuff, Auggie, Continue, Antigravity, Trae, MiMo Code, Qwen Code, Mistral Vibe, Kimi, Rovo Dev, Devin, Autohand, Ante, Pi, oh-my-pi, Prime Agent, Hermes, Command Code, OpenClaude, OpenClaw - whichever of them you have installed. Or any other CLI agent: point it at your own command and that works too. Foxl finds them itself, and it finds the ones a simple name check misses, because half of these ship under a different binary than their product name (Kiro is
kiro-cliand needschat --tui, Cursor iscursor-agent, Continue iscn, Antigravity isagy). It runs as you, on your own files, with your own credentials, and nothing goes through the relay. A blank terminal is still one click away if that is what you want. Reopening a pane reattaches to the agent you had, with its scrollback, rather than starting a new one. Dispatching an unattended Foxl task to this machine is still not available (that is the clone-and-push half) and the machine row says so. - Foxl Code tasks can run on a machine you own. Settings > Code > Machines is new. Foxl Cloud stays the default, stays free and is unchanged; alongside it you can now launch an EC2 box in your OWN AWS account, or point Foxl at a server you already have. Your CPU, your disk, your VPC, your bill. The launch runs in your terminal with your own
awsCLI, so Foxl never holds a credential for your account, and the box opens no inbound port: it dials out over a Cloudflare tunnel, so there is nothing to expose and no certificate to install. The wizard shows a monthly estimate for three usage shapes with the price basis attached and a link to AWS's own calculator, and it says plainly that the disk keeps billing while the instance is stopped. - A machine picker in the Code header, and a per-repository pin. The header names the machine your next task will run on and lets you switch it - one list of your machines, with a single "Always use this for \<repo\>" row under it. You can pin ONE repository to ONE machine, which is what makes unattended work land in the right place: schedules and the autonomous loop never pick a machine themselves, so before this a repository that needed the x86 box could only be steered by hand. Precedence is the task's own choice, then the repository's, then your account default, then Foxl Cloud, and anything that falls through is recorded. The dropdown decides where the NEXT pane or task goes, so it never reshapes a terminal you already have open.
- Foxl Cloud is a terminal now, not a form. Picking Foxl Cloud (or your own box) in a Runtimes pane used to give you a box that said "What should this agent do?" and a Run button - a way to file a job, next to a pane on your own Mac that was a live agent you could talk to.
Cmd+Tmeant two different things depending on a dropdown, and only one of them was a terminal. Now both are the same: the pane opens the coding agent on the machine you picked and you type at it, in the cloud exactly as on your own computer - and it keeps going when you close your laptop. Choose which agent, on either machine: Claude Code, Codex, Kiro, Cursor, Hermes or OpenCode, right under the terminal, with a note beside any that still needs a key of yours. Closing a cloud pane does not open a pull request - your work is pushed to that session's own branch and nothing is announced; the agent opens a PR when you ask it to. - Your default coding agent was being ignored unless it was one of three. If you set Foxl Code's agent to Cursor, Hermes or OpenCode in Settings, it was saved, it was shown back to you as your choice, and then every task ran Claude Code anyway. Asking for one of those three per task was accepted and quietly ignored the same way. All six work now, from Settings and per task.
- A terminal workspace for watching runs. Runtimes is a workspace of live terminals now rather than a list of cards, and the panes are a real split layout:
Cmd+Tadds one where it fits,Cmd+Dsplits right,Cmd+Shift+Dsplits down, and dragging the line between two panes resizes them. Up to six, and each one either starts a new agent or attaches to a run already going, so two attempts can be watched side by side.Cmd+Shift+Enterzooms one pane to the whole workspace and back,Cmd+[/Cmd+]move between panes, and Equalize puts every divider back in the middle. Your arrangement survives leaving the page and coming back.Cmd+Fsearches a terminal's 5,000 rows of scrollback. Each pane carries that run's status, backend, model, branch and live spend, and its Stop / Open on GitHub / Copy task id actions. On a phone the workspace shows one pane at a time instead of splitting, since two panes on a phone are too narrow for a CLI to lay itself out in.
Changes
- Stop now really stops a task running on your own machine. Stopping used to update Foxl's records and leave the coding agent running on your box until its time limit, so it kept spending your CPU and your model credit after you asked it to stop. It commits and pushes what it has, then exits.
- Keyboard shortcuts are a settings screen now, and they can be changed. Settings > Shortcuts lists every key Foxl listens for, grouped by product and searchable - type "delete" or type the keys themselves. Press Record on a row, press the combination you want, and that is the shortcut; there is a Reset on any row you changed and a Reset all at the bottom. If two commands that share a screen end up on the same combination, both rows say so and name each other, rather than one of them quietly winning. The keys are printed the way your system writes them - ⇧⌘F on a Mac, Ctrl+Shift+F on Windows and Linux - from one saved setting, so a shortcut you set on one machine reads correctly on the other. Rows the app sets itself (saving a file, a new line in the composer, the ones the editor and the terminal own) are listed too, marked as such, because half of what this screen is for is answering which keys are already taken. Two rows are waiting on the feature they belong to: running a task in the cloud, and running it on this computer.
- Dictate anywhere has its own transcription provider and language. Settings > Notes > Dictate anywhere gains a Transcription provider row and a Transcription language row, just above the cleanup model. Until now dictation quietly used whatever the recording group above it was set to, and nothing on any screen said so - which is the wrong shape for it, because a meeting is an hour of audio once while a dictation is a few seconds of it many times a day. So the two are separate picks now: send dictation to Amazon Transcribe on your own AWS account or to Foxl Relay, independently of what your meetings use. The AWS region stays shared, since one account has one region. The language defaults to Auto-detect, which is Amazon's own language identification - see the fix below.
- Foxl Code search is the app's own search bar now, not a separate popup. Cmd+Shift+F in Foxl Code opened a task-search palette of its own, which on the Mac desktop stacked on top of the search bar at the top of the window - two search boxes for one keypress, neither aware of the other. That palette is gone. Your tasks are a group in the search everything else already uses, alongside pages, conversations and workspace files, with a Tasks filter beside the existing ones; picking one opens it exactly as clicking it in the list does. On the web, Windows and iPhone, where there is no search bar in the window, the shortcut opens the same centered overlay Chat uses and tasks appear there too, so nothing lost a way in.
- First run now ends by asking you to choose a model provider. The setup tour gains a fourth and last card, which opens Settings > Model & provider and points at the provider picker. On a fresh desktop install nothing is configured yet, so the card before it - "Start your first chat" - hands you a composer whose first message comes back as a provider error, and nothing on that screen said where the models come from. The tour closes on that pane and leaves you there, on the one thing that has to happen first. Not shown on app.foxl.ai, which has no provider to set up.
Fixes
- The Terminal works again on Windows too, and typing is the part that was broken. Since v0.6.10 a terminal on Windows showed you the PowerShell prompt and then ignored everything you typed - the output side worked, the input side went nowhere, so the session just sat there. Same root cause as the Mac fix below (v0.6.10 changed the engine the local server runs on) but a different half of it, and one that could not be fixed the same way. Windows now runs the server on the previous engine instead, which restores the terminal. The one trade: searching your conversation history on Windows goes back to the slower method it used before v0.6.10.
- On the web, the cookie notice no longer covers the first thing in the sidebar. On a first visit to app.foxl.ai or code.foxl.ai the analytics notice sat across the whole width just under the header - and the sidebar's top row, Chat, was entirely behind it and could not be clicked until you answered the question. On code.foxl.ai it also covered the page title. The notice now starts where the content does, so the navigation stays usable while it is up. Nothing changes in the desktop app, which never shows it.
- Settings no longer cuts off which transcription provider you picked. "Amazon Transcribe (your AWS account)" was clipped mid-word - with no ellipsis, so there was nothing to say it had been cut - in both Settings > Notes > Recording and Settings > Notes > Dictate anywhere. Those two rows now size to their own text, in every language.
- "Dictate anywhere" no longer appears in Settings where it cannot work. On the web and on the phone the row was listed and opened a screen with a heading and nothing under it: the feature needs to hold a key down for you in other apps, which only the desktop app can do. The row now appears only where it does something.
- The Dictate anywhere screen does not say its own name three times. The pane title, the section title and the first row all read "Dictate anywhere"; the duplicate section title is gone.
- The Terminal works again on macOS. Since v0.6.10 opening a terminal on a Mac gave you a cursor and nothing else: the session really started, but no output ever came back, typing produced nothing on screen, and a shell you exited still showed as running. It was not the terminal itself - v0.6.10 changed the engine Foxl's local server runs on, and on macOS that engine cannot read a terminal's output the way the previous one did. It is read a different way now. Linux was never affected, and Windows uses a different mechanism again. Everything the terminal does is now verified end to end on the engine the app actually ships with, including a 338 KB burst of output, so this cannot go quietly missing again. Nothing else was affected, which is why it took four releases to notice.
- Settings > Appearance > "Reset onboarding" no longer starts setup on top of Settings. It returns to the main screen and restarts setup there. It used to reload the page it was on, so the look picker opened over the settings form you were still reading - startling, and the wrong screen behind a flow whose tour points at the sidebar.
- A tour card no longer misses a control that mounts late. All four tours re-measured for 400ms after switching surfaces and then gave up, so a card whose subject is a lazily loaded page landed in the corner pointing at nothing. They wait for the control to arrive now.
- Dictate anywhere tells you which key to press on Windows and Linux. Holding a key needs something only macOS provides, so on those two systems Foxl quietly used a shortcut instead - Control+Alt+D - and the settings screen printed it nowhere. The note above it even said "the shortcut below is being used instead" with nothing below it, and the switch's own description said the feature "can only listen once macOS allows it", which is not a sentence that means anything on a PC. The shortcut is now a row you can read and change wherever holding a key is unavailable, and the wording says what is actually true on the system you are using. Nothing changes on a Mac.
- Linux says what it needs in order to type. Typing into another app on Linux goes through
wtype,ydotoolorxdotool, none of which Foxl ships - so on a machine with none of them dictation put the text on your clipboard and never explained why. Settings > Notes > Dictate anywhere now names them. - Linux stops reserving space for macOS window buttons. The strip along the top of the window kept 78 pixels clear at its left end for the close/minimise/zoom dots, which only macOS draws there - on Linux the window manager draws its own title bar above that strip, so those 78 pixels were empty and the sidebar button floated in from the edge for no reason. Reclaimed. macOS is unchanged, and Windows never had the strip at all.
- The Linux download no longer carries Windows and macOS binaries. The AppImage included the Windows console helpers and both other platforms' native modules - about a megabyte of files Linux can never load, including Windows debug symbols. They are removed from the Linux build, which is smaller for it. Nothing about how the app runs changes.
- Foxl starts on Windows again without Node.js installed. Since v0.6.10 the app has shipped its own JavaScript runtime for the background server, and on Windows it then refused to use it: a machine with no Node.js or Bun of its own showed "No JavaScript runtime was found for the Foxl server - the install may be damaged", which reinstalling could not fix, because the reinstall put back the same working file. Nothing was damaged. The runtime is named
node.exeon purpose - the "keep running when closed" service records that exact path when you switch it on, so renaming it would strand those installs - and Bun treats being callednodeas a request to behave like Node, in which mode it declines to print its version. That version check was how Foxl decided the file was usable. It asks a different question now, one both runtimes answer under any name. macOS and Linux were never affected: their copies are namednode-arm64andnode-x64, which is not the name that changes Bun's behaviour. - "Auto-detect" detected nothing and transcribed everyone as English. It really detects now. The transcription language row offered an Auto option that asked Amazon Transcribe for
en-USoutright, on both the live-recording and drag-a-file-in paths. So a Korean or Japanese speaker who chose it got a meeting transcribed as English: billed, with no error anywhere, because the connection opens normally and the words come back as nonsense. It reads as Transcribe being bad at your language rather than as Foxl asking for the wrong one. Measured on 5.8 seconds of Korean, the old path returned "And young Isa's semida." Auto is now Amazon's real language identification, and it leans toward your app language so it settles on the right one from the first words instead of guessing for a second. Verified against the real service end to end: the same Korean audio comes back as "안녕하세요, 오늘 회의는 3시에 시작합니다. 미리 준비해 주세요. 감사합니다.", and English audio is still detected as English even with Korean set as the preference - so it is identifying what you actually said, not just trusting the app. It is the default for a new install, it covers recordings, dragged-in files and Dictate anywhere through one shared definition, and it works whether you transcribe on your own AWS account or through Foxl Relay. If you had already picked a specific language, nothing changes. - The transcription language row claimed changes apply mid-recording. They never did - the language is fixed when you press record, including across the reconnect that happens every few minutes, because Amazon binds it when the connection opens and switching would mean losing whatever was mid-sentence. The description now says it applies to your next recording.
- Changing the dictation language did nothing until Foxl was reopened. The dictation pill read the language once, when it was pre-warmed in the background, and never looked again - so a language picked in Settings only took effect on the next launch, while the cleanup model beside it applied immediately. Both are now read at each press.
Features
- Live translation has its own model picker. Settings > Notes > AI features gains a Live translation AI model row, under the target language, so the model that translates each incoming line is yours to choose. It was already a different model from the one that writes your summaries: translation runs once per spoken segment while you are reading the line, so it has always run on a small fast model (Haiku 4.5), where a summary reads the whole transcript once and wants the best model you have. There was just no way to see that or change it. Pick anything your Model & provider can serve, or "Same as Model & provider" to follow Foxl Agent.
Changes
- "AI model" is now "Summary AI model", and "Translation model" is "Live translation AI model". That pane picks a model for three separate jobs - summaries and coaching reports and the meeting copilot, note titles, live translation - and the row labelled just "AI model" gave no clue which of them it was.
- The Model provider row and the Foxl Agent model card are gone from Notes AI features. Both repeated what the section heading above them and the model row below them already said, which is that Notes AI runs on the model you set for Foxl Agent unless you pick one here, and neither could be operated. What survives is the part nothing else on the pane could tell you: when no desktop answers, or the one that answers has no provider set up, the pane says so and offers the door that fixes it.
- One first-run flow instead of two. The old welcome card - the "Welcome to Foxl" feature list, the "Install Chrome Extension" step, and the sign-in / download-the-app step - is gone. A new install now opens straight into the appearance-based setup (pick a look, answer two quick questions), which is the flow that already sets something the app then uses. The Chrome extension still installs from Settings > Web access, and sign-in still appears the moment you do something that needs an account.
- Chat on the web and on the phone says what to do instead of failing. foxl.ai serves no model inference, so a message sent with your desktop unreachable had nowhere to go: it travelled to foxl.ai, was refused there, and came back as an error after the wait. It is answered on the spot now, and the answer names the fix - open Foxl on your computer, where your model is configured once for all three products: your own provider key, a local model, or the Claude or ChatGPT subscription you already have. Nothing else about that screen changes. Your saved conversations still open and read exactly as before while the desktop is away, because that has never needed a model.
Fixes
- Attaching a PDF to GPT-5.6 or Grok 4.6 works. The same file that Claude read without complaint came back
400 Unsupported file type: '<your file name>'on every model served through your AWS account's OpenAI-compatible endpoint - GPT-5.6 Sol/Terra/Luna, GPT-5.5, GPT-5.4 and Grok 4.6 - and the name in that message was missing its.pdf, because Foxl had removed it. Amazon's Converse API (which is how Claude receives a file) refuses a document name containing a dot, so Foxl strips the extension and states the type in a separate field; the OpenAI-style endpoint has no such field and reads the type off the file name alone, so stripping it left nothing to read. The extension is now kept for the models that need it and still removed for the ones that reject it. Measured against the real endpoint with one PDF per model: all six answered 200 and quoted the document's contents back. The same check ran for every file type Foxl can attach: PDF, DOCX, XLSX, CSV, TXT, MD and HTML all read correctly on those models now (as do JSON and XML, which travel as text). - A file those models cannot read says so in words. The two exceptions are
.docand.xls- the pre-2007 Office formats - which AWS's OpenAI-compatible endpoint does not accept even though Claude reads them without trouble. Attaching one used to surface a raw400; Foxl now names the file and tells you to re-save it as.docxor.xlsx, or to ask a Claude model instead. A file in some other binary format gets the same treatment rather than being sent as if it were text.
Features
- Dictation can ask AWS to hurry. Settings > Notes > Dictate anywhere has a Service tier row when your models come from your own AWS account: Priority is the fastest and costs more per token, Flex is the cheapest and the slowest, and Standard stays the default because the premium is a decision rather than a default. Measured on Gemma 4 31B, Priority answered in 499ms against Flex's 1597ms, and it is the single biggest lever on how long you wait with your cursor in another app - bigger than the choice of model. It applies to the AWS models served through Bedrock's OpenAI-compatible API (Gemma 4, GPT-5.x, Grok), and the row only appears when your Model & provider is your own AWS account. If you then point dictation at a model somewhere else with the Cleanup model row below, the tier is simply not sent - the setting follows AWS, not the row above it.
- "Wait for the full answer" is a setting now, on by default. That is what dictation cleanup has always done, and it is the faster of the two: the tidied text arrives in one piece, which is how it has to be pasted anyway. The switch is there for the case that needed it - a model server of your own that refuses a request like this, since some answer only a streaming one. What lands in your document is the same either way.
- Notes, Code and Settings explain themselves the first time you open them. Foxl already gave the Agent product a short walkthrough on a new install - three cards, each anchored to the sidebar row it is about. The other two products got nothing, so Notes opened as an empty editor and Code as an empty task list with no obvious first move. Both now get the same three cards on their first visit, and the control each card describes is ringed while you read it. Settings gets two, and they point at the two features that only live there and that almost nobody found: installing the browser extension, so Foxl can work in the tabs you already have open instead of a separate window, and Dictate anywhere, where you hold one key and talk into whatever app has focus. Each card selects the pane or page it is talking about, so you are reading it against the real thing rather than a description of it. Once each, dismissable with Escape, and both of the Settings > Appearance buttons below bring them back. A tour never appears over a pane you asked for by name - a jump from the command palette lands where you sent it.
- A Reset onboarding button, in Settings > Appearance. It sits under "Show setup again" and does the other half of that job: rather than replaying the look picker and the tour, it forgets that setup was ever finished on this device and reloads, so the welcome screens start over from the beginning - including Foxl Code's setup page. It works on the desktop app and on the phone apps too, which normally skip the welcome card: asking to see it is a different thing from arriving for the first time. Nothing on your account changes and your other devices are untouched, so there is no confirmation to click through - and you can find it by typing "reset onboarding" into the command palette.
- Notes and dictation can each run on their own model now, from any provider you have configured. Settings > Notes has three model rows - AI model (summaries, coaching reports, the meeting copilot), Title model (auto title) and Cleanup model (dictate anywhere) - and each is an independent choice. They all default to following Foxl Agent, so nothing changes until you pick something. The point is that these jobs want different models: a summary reads a whole transcript and wants your best model, while a title is one short line and a dictation cleanup is a punctuation pass you wait on with your cursor in another app, where the cheapest fast model wins. Each dropdown lists everything your configured providers can serve, grouped by provider - your AWS account, your Anthropic or OpenAI key, a local Ollama, and your Claude Pro/Max or ChatGPT Plus subscription - with the provider you are already on first. Picking one here does NOT move your agent chat; it moves only that one row. Two consequences worth knowing: your Claude Max or ChatGPT Plus plan can now write meeting summaries at no extra cost, and auto title has a control at last (it was always a fast model, hardcoded, with no way to see or change it).
Fixes
- Gemma 4 answers in chat now. Picking it still failed with a wall of text about a JSON-RPC error, or with "the provided model identifier is invalid" - which one you got depended on where you picked it. Both are fixed. The previous release fixed this for scheduled and autonomous runs only: chat, the Chrome extension, channel replies and scheduled tasks each build their own request, and the fix had only reached one of them. All of them share one rule now, and a build check keeps it that way.
- Gemma 4 and the other AWS models are in the model lists. Settings > Model & provider offered nothing under "AWS Bedrock" for Gemma 4, GPT-5.x or Grok 4.6 - they were only reachable by knowing to switch the provider dropdown to a second AWS entry - and Settings > Notes > Dictate anywhere's Cleanup model dropdown left them out entirely, while telling you a model you had already chosen was "not served by the provider" and to pick from a list that could not contain it. All three now appear wherever the chat model selector already showed them, because they run on the same AWS account.
- Gemma 4 says what it is for. It was the one model in the chat picker with a blank description line. It now reads "Fast text cleanup and summaries - no tools", which is also the useful warning: it is the cheap fast model for tidying text, and it is not the one to pick for work that needs the agent's tools.
- The Input Monitoring permission has its own row now, like the typing one. In Settings > Notes > Dictate anywhere, "Allow Input Monitoring" used to be a loose button under a warning, and it only appeared while the feature was switched off or while you had picked "Hold a key while speaking" - so if you were on the press-to-start fallback because macOS was blocking the key, there was no way to grant the permission from that screen at all. It is a labelled row beside the typing permission now, on screen whenever the grant is missing. Pressing it still puts Foxl in the Input Monitoring list and opens System Settings at that exact pane, so there is a switch waiting to be turned on.
Changes
- Amazon Bedrock is one provider again. Settings > Model & provider listed it twice - "AWS Bedrock" and "Bedrock Mantle (OpenAI + Grok)" - over the same AWS account and the same credential, because Bedrock serves Claude and GPT-5.x over two different APIs internally. That is our plumbing, not something you should have to pick: choosing GPT-5.6 meant switching provider, and switching took every Claude model off the model list, so the two halves of your own account could never be seen together. There is one Amazon Bedrock row now, and its Default Model dropdown lists everything your account can serve - Claude, GLM 5 and Kimi K2.5 alongside GPT-5.x, Grok 4.6 and Gemma 4. Nothing to re-configure: a desktop already pointed at the Mantle entry lands on the merged row with its model intact, and the request still goes over whichever API that model needs. The dictation Cleanup model list gains the same models for the same reason - it was hiding exactly the small fast ones that row is for.
- The Cleanup model dropdown matches the Default Model one. Settings > Notes > Dictate anywhere listed every option as two lines, the name over its id in monospace, which made the closed control a two-line block sitting in a row of single-line ones. Options are one line with a short provider badge now, and the selected model's id reads underneath the dropdown - the shape Settings > Model & provider has always used.
Features
- Dictation can run on its own model now, picked from a list. Settings > Notes > Dictate anywhere has a Cleanup model row again, and it is a dropdown rather than a box you type an id into: it offers the models your configured provider actually serves, the same list Settings > Model & provider builds. It defaults to Same as Model & provider, so nothing changes until you choose - and choosing does not move anything else, because chat and dictation keep separate selections. The point is that the two jobs want different models: dictation is a short mechanical tidy-up you wait for with your cursor in another app, so the cheapest model that can punctuate a sentence beats the smartest one available. Only the list is shared; the choice is per surface. If a model you pinned earlier is not one your current provider serves, the row says so and the cleanup falls back to that provider's own model rather than failing.
Fixes
- "Allow Input Monitoring" opens System Settings now. In Settings > Notes > Dictate anywhere, that button asked macOS for the permission - and macOS only ever asks you once per app, so every press after the first did nothing at all and the button looked broken. It now takes you straight to Privacy & Security > Input Monitoring, where the switch actually is. The "Open settings" button for the typing permission does the same.
- Dictate anywhere cannot be switched on while macOS is blocking it. The whole feature is a key you hold, so if macOS has not allowed Foxl to notice that key the switch is greyed out and says why, with the button to fix it beside it - instead of turning on and then quietly doing nothing. If you already had it on, you can still switch it off.
- Gemma 4 and Notes AI work again on the default setup. Picking Gemma 4 for chat failed with a cryptic "Generation failed" error, and on a fresh install every Notes AI feature - summaries, auto-titles, the dictation cleanup - reported "the provided model identifier is invalid", because the request used the display model name instead of the id Bedrock expects. Both are fixed: Gemma 4 now answers in chat, in Notes and in dictation, and so does whatever model your Agent is set to. Gemma stays a fast plain-text model (it does not drive the agent's tools), which is what it is built for.
- Recording works again on iPhone and Android. Pressing record on a phone opened the microphone and then stopped, with a message about connecting a desktop. Transcription had moved onto your own AWS account, which the Foxl desktop signs for using the credentials on that computer - so a phone could only record while a computer was awake and reachable, and otherwise could not record at all. A phone now falls back to Foxl's own transcription when there is no computer to borrow credentials from, which is how it worked before, including recording that keeps going on iOS when you switch apps or lock the screen. Nothing changes on the desktop, or on a phone that can reach your desktop: those still transcribe on your own AWS account.
- A refused microphone says what to do about it. Instead of the browser's raw "Permission denied", the message now names the permission to allow. Android also no longer claims "no microphone found" before the permission prompt it needs has even been answered.
- Dictate anywhere now actually types the result, on all three platforms. The paste was the one step that could fail without saying so, and it failed three different ways. On macOS it went through an AppleScript keystroke, which needed a second permission beyond the one the settings screen asks for and, when it did run, could report success while typing nothing at all - measured 0 out of 16 attempts. It now posts the keystroke directly, needs only the Accessibility permission you were already asked for, and can answer whether it holds it instead of guessing: 13 out of 13 in the same test. On Linux the first tool tried was one that cannot work on a Wayland desktop (the default on current Ubuntu, Fedora and Debian), and it left the Control key held down afterwards, so your next keypress became a shortcut - the Wayland tool is tried first there now, the modifier is released, and a third tool covers desktops with neither. On Windows there is a second mechanism for machines where company policy blocks the first.
- The pill tells you when it could not type. A failed paste used to close the pill exactly as a successful one does, so dictation appeared to work and the words simply never arrived. It now says the text is on your clipboard, and a missing permission still says which permission.
- Your clipboard is no longer risked on a paste that will not happen. Foxl checks that the dictation really reached the clipboard before pressing paste - if another app is holding the clipboard open, pressing paste would have typed whatever was there before, which can be something you never meant to paste.
Features
- Dictate into any app, not just Foxl. Settings > Notes > Dictate anywhere: hold a key, say what you mean, let go, and the tidied-up text is typed straight into whatever had your cursor - a mail draft, a terminal, someone else's chat box. The default key is Fn (Globe), and you can pick another (either Command, Option, Control or Shift key - left and right are separate - or F13-F15). A small pill appears at the bottom of the screen while it listens, showing a live waveform, then a spinner while a model cleans up what you said: filler removed, your own corrections applied ("Thursday, wait, make that Friday" becomes Friday), punctuation and lists added. The cleanup runs on whatever Settings > Model & provider is pointed at, the same model as chat. On by default, but it cannot hear anything until you allow it: macOS asks for two separate permissions - one to notice the key you are holding, one to type the result into other apps - and the settings screen tells you which one is missing. Until Foxl can type, the text is put on your clipboard instead, so nothing you said is lost. There is also a press-to-start / press-to-stop shortcut for setups where holding a key is not available. Desktop only, macOS for now.
- Google's Gemma 4 31B is selectable now, on Bedrock with your own AWS account. It is the cheap fast tier rather than a frontier model: $0.14 per million input tokens against Opus's $5, 256K of context, and a complete short answer in under half a second. Useful wherever you want a quick mechanical answer instead of a considered one. Available in all four Bedrock regions that serve it, so nothing is pinned. The two smaller variants Bedrock also serves are deliberately not offered: the smallest transcribed a self-correction instead of applying it on all ten test runs, and the middle one is correct but slower than the 31B at the same price.
- The Chrome extension is on the Chrome Web Store now, so installing it is one click. Until today the only way to give Foxl your real browser was the manual one: download a zip, unzip it, open
chrome://extensions, turn on Developer mode, choose "Load unpacked" and point Chrome at the folder - and then live with Chrome asking you to disable developer-mode extensions on every startup. Foxl now offers the store listing where you need it: a step in onboarding, and the primary button in Settings > Web access. Chrome keeps it up to date on its own and the startup warning is gone. The manual route is still there as the second button - it downloads the extension's source and you load that folder - because a store install is not always possible: an enterprise policy can block the Web Store, some Chromium forks do not carry it, and the store build is one review behind a release. - Foxl Notes AI now runs on the model you set for Foxl Agent. Settings > AI features no longer has a model of its own: summaries, auto-titles, coaching reports, the HUD ask and live translation all use whatever Settings > Model & provider is pointed at - Bedrock with your own AWS account, an Anthropic / OpenAI / Google API key, a Claude Pro or ChatGPT subscription, Ollama, or any OpenAI-compatible endpoint. One setting for chat, Notes and Code, and the panel names the provider and model it will use with a link to change it.
Changes
- Conversation search is about 3 times faster, because the agent server runs on Bun now. Foxl ships its own JavaScript runtime for the background server, and that runtime changed from Node 22 to Bun 1.4. The reason is search: the Node build Foxl shipped had no SQLite full-text support, so searching your conversations scanned every message in the database every time. Bun's does, so the full-text index Foxl has always tried to build finally builds. Measured on a real 1 GB library of 1,967 conversations and 25,256 messages, the same eight searches took 591 ms instead of 1,820 ms, and returned exactly the same results. Searching also stops getting slower as your history grows. The first launch after this update is a one-time exception: it builds that index, which took about 2.4 seconds longer than a normal start on the library above and added about 36 MB to the database. Every launch after that is back to normal. Nothing about your data changes and nothing needs re-importing, in either direction - going back to an older Foxl works too.
- Smaller download, less CPU. The macOS download drops by about 16 MB and the installed app by about 81 MB, and Linux by about 39 MB, because the new runtime binary is smaller. The server also uses roughly a third less CPU for the same work, which matters most on a laptop where Foxl runs all day. Two costs in the other direction, both measured rather than estimated: Windows grows by about 6 MB, where the old runtime happened to be the smaller of the two, and the server holds about 23 MB more memory while sitting idle. Startup time is otherwise unchanged.
- The Flag control on an AI reply is switched off. Reporting a model's response was added in v0.6.9 and is now behind
AI_RESPONSE_REPORTS, off, so no Flag button appears in an assistant turn's action row on Foxl Agent or Foxl Code. Reporting and blocking a message written by another PERSON, in channels and DMs, is a separate control and is unchanged. Nothing is deleted: the dialog, the relay route and its migration all stay, so turning it back on is one line. - A reply no longer carries the time of day. In a transcript you read top to bottom,
07:06 PMunder every answer was information nobody asked for. The usage receipt it used to open now has its own button, beside Copy and Regenerate.
Fixes
- Voice dictation no longer loses the first words you say. The microphone opens before the transcription socket does, and every audio frame captured while that socket was still connecting used to be thrown away - so anything spoken in the first fraction of a second was simply gone. The audio is held and sent the moment the socket is live. Most noticeable if you start talking immediately rather than waiting for the mic button to light up.
- Dictation is faster from the moment you start speaking to the moment the text appears. Three things, each measured: audio now reaches the transcriber in 64ms frames instead of 256ms ones; the wait after you stop talking ends when the transcriber says it is finished rather than after a fixed 1.5 seconds; and the partial-results stabilization that Notes needs for a live meeting transcript - and that dictation never reads - is switched off there, which alone took the first words on screen from 1145ms to 626ms. Meeting transcription is unchanged.
- The token counts in the titlebar panel and the ones on a reply now say the same thing. They were two readings of the same rows and they disagreed in public: for one turn the panel reported
Input tokens 90.6KandCached 90.6K (100%)while the reply's own receipt reportedInput 4,Cache read 45.1K,Cache write 45.4K - the panel had added the three counts together and called the total "Input", then printed the same total again on a second row. The context readout split the same way:
5%in the panel,45.4K / 1.0Mon the reply. The panel now renders the reply's receipt, so there is one set of labels, one formatter (a million tokens used to print as "1000.0K" in the titlebar) and one pair of thresholds on the context bar. Only the heading differs: the model on a reply, "This chat" or "Usage - last 24h" in the panel. - "Auto-approve all tools" is on by default again, and it now actually applies. A fresh install approves every tool call without a prompt, as it did before v0.6.9, because an approval modal stalls the runs Foxl exists to do unattended - a schedule, a request from your phone, a Slack mention - and a stalled tool call times out and reads as a failure rather than as a question. Two things changed: the default is seeded once per install, so turning the switch OFF now stays off across restarts instead of coming back at the next launch; and the switch is no longer ignored when the app sends its first message immediately after start-up, which could leave Settings showing it ON while
execand the terminal kept asking for the rest of the session. - Foxl Relay is no longer offered where it cannot answer. foxl.ai serves no model inference, so it is gone from the provider list in Settings > Model & provider and from the Notes AI provider row instead of sitting there marked "Ready" and failing on use. Nothing is removed: the option comes back on its own if hosted inference is switched back on.
- A desktop that is not reachable now says so when Notes asks it for a summary, instead of quietly writing an empty one over the note.
- Recording on the iPhone is back to the microphone alone. v0.6.9 added system-audio capture through iOS's broadcast sheet, so every recording asked you to confirm a second dialog before it could hear the other side of a call. It is removed: press record and it records, with no extra tap and nothing to dismiss. The "Capture system audio" setting goes back to being a Mac-only option, where the system grants the capture without asking. Nothing else about an iPhone recording changes - it still keeps running with the screen locked, still transcribes live, and still saves audio you can tap a line to replay.
- Stopping a recording works again on a phone. The tab bar at the bottom of the screen is a floating capsule that the page passes underneath, and Foxl Notes was the one screen that never made room for it - so the Stop button sat under the tab bar, and tapping Stop navigated you somewhere else instead of ending the recording. The whole Notes screen now ends above the tab bar, which also brings back the last row of the recents list on the Notes home and the bottom of the Add-context screen.
Features
- Foxl Notes can now run its AI on a model you host yourself. Settings > AI features has a Model provider row: keep Foxl Relay, or pick Self-hosted and give Notes the base URL of any OpenAI-compatible endpoint - Ollama, vLLM, LM Studio, llama.cpp's server - plus the model name it serves, and an API key if your server wants one. Summaries, auto-titles, coaching reports, live translation and the floating meeting copilot all go there instead of to foxl.ai, and none of it needs a Foxl account: the AI switches turn on as soon as the URL and the model name are both filled in. Recording and transcription are unchanged. The model list and the translation-model picker are hidden while Self-hosted is selected, because your endpoint serves the one model you named rather than a catalog.
- Foxl Notes transcribes with your own AWS account now, and it finds your credentials by itself. Settings > Recording and transcription has a Transcription provider row set to Amazon Transcribe, and there is no key to enter: Foxl signs the connection with the same AWS credentials the rest of the app already uses - environment variables,
~/.aws, the profile you picked under AWS Authentication, a live SSO session,credential_process, an instance role. The panel says which one it found and offers a Re-check, so you can tell a working setup from an expired SSO session before you press record. The only thing you set is the region. Everything downstream is unchanged - live captions, speaker labels, click-to-play timestamps, dragging an existing audio or video file in to transcribe it - and none of it needs a Foxl account or Foxl credits, because Amazon bills you directly for the audio. Foxl Relay is still listed as the other provider but no longer transcribes; if that was what you were using, choose Amazon Transcribe to keep recording. - Foxl Code runs on your own Claude or ChatGPT subscription now. Foxl does not sell model access, so cloud coding tasks had nothing to reach a model with. Open Foxl Code > Settings and you will find Agent credentials: if Foxl Desktop is running on the same computer, press Sync and it hands over the Claude Code or Codex login you already have there - nothing to copy, nothing to paste. On the web you can upload the credential file instead. Your subscription is stored encrypted, is never shown back to you, and is deleted from the task machine the moment the agent finishes. Foxl keeps track of when it expires and renews it on its own where it can; where it cannot, the Settings page says so rather than letting a task fail for a reason you cannot see.
- Bring an Anthropic API key instead, if you would rather. Same section. It works for Claude Code and it is what the OpenCode assistant uses - it cannot use a subscription, because only Claude Code and Codex can sign a request with one.
- Every assistant now says why you cannot use it. The picker used to grey a backend out with "Not provisioned" whether it was genuinely unavailable or simply waiting on a key of yours. Those are different problems, and only one of them is yours to fix, so they now read differently - and Kiro, which was switched off wholesale, is available to anyone who adds their own Kiro key.
- You can now report a message and block the person who sent it. Channels and DMs let one person's text reach another, and until now there was nothing to do about a message you should not have received. Long-press any message from someone else on a phone, or use its
...menu on a computer: Report opens a short form with a reason and an optional note, and Block stops the two of you seeing each other's messages and stops either of you starting a new direct message. Blocking is reversible from Settings > Account > Blocked accounts, and the person is never told. The report form can do both at once, since somebody who has just been harassed usually wants both. - AI replies are reportable too. Every response now carries a flag button beside Copy, using the same reason list. Replies come from third-party models that apply their own safety filtering, and this is how you tell us when that was not enough.
- Settings > Appearance > Surface now offers fourteen washes instead of two, grouped like a colour picker. Dawn and Mist are still there, under Washes, alongside Sand and Sage. Colors is a row of six single-hue tints - Blue, Violet, Pink, Coral, Crimson, Green - for when you want the sidebar a particular colour rather than a particular mood. Spectrum is new and holds the gradients that run through more than two colours: Rainbow, Trans, Sunset and Aurora. Every swatch paints the real thing, so the tile you press is the colour you get, and picking one still leaves the reading area untinted. Each of the fourteen was tuned so the sidebar's labels keep exactly the contrast they have with no wash at all - turning this on never costs you legibility, in light mode or dark.
- Settings > Notifications is now one screen for every kind of notification Foxl sends. It used to be two different screens behind the same name, and which one you got depended on the device you opened it from: on a computer you saw the banner rules for that machine and could not see or change what gets pushed to your phone, and on a phone you saw the push categories and could not see the banner rules. Neither screen mentioned that the other existed. Now there is one screen, grouped by where a notification actually arrives: this device, your phone, and email. Your phone's push categories are settings on your account rather than on the handset, so you can turn one off from your desktop without picking up your phone.
- Email notifications can finally be changed from inside Foxl. Product updates and tips were only adjustable through the link at the bottom of one of those emails, so if you had deleted it, or never got one, there was no way to change them, even though the page that link opens told you to manage them in your account settings. Both are in Settings > Notifications now, along with a clear notice if you have previously unsubscribed from everything, and a way back if you want to. Sign-in links, receipts and payment problems are part of the service and are not listed, because they cannot be turned off.
- Messages from any connected channel can be silenced. Foxl supports 41 channels, and only five of them had a switch. A message arriving from Signal, Matrix, Teams, LINE, IRC or any of the other 36 always raised a notification with no way to decline it. There is now an "Other channels" switch covering all of them.
- Foxl Notes on iPhone can now record what you HEAR, not just your microphone. With earphones or AirPods in, the other side of a meeting plays inside your ears and never reaches the microphone, so an iPhone recording came back one-sided - your voice and nothing else. The desktop app has captured both sides for a while; iOS now does the same. Press record and iOS asks you once to confirm; from then on the audio of whatever you are listening to (meeting apps, calls in other apps, voice messages) is mixed into the same transcript as your own voice, with one speaker stream, so nothing is transcribed twice. Decline the confirmation and the recording simply continues from the microphone alone, exactly as before. Turn it off under Notes > Settings > Recording > Capture system audio. Two limits worth knowing, both set by iOS and not by Foxl: cellular phone calls cannot be captured at all, and copy-protected playback is muted by the system.
- Recording from your phone or from app.foxl.ai no longer leans on your computer every few minutes. Amazon caps a transcription connection at five minutes, so a recording quietly reopens one throughout a meeting - and on a phone each of those used to be a round trip to your desktop, in the middle of the audio, with the recording stopping if the machine went to sleep. Your computer now hands the phone a one-hour pass that can do exactly one thing, start a transcription, and the phone reopens its own connections locally after that: nothing to wait for, and an hour-long meeting keeps recording even if your desktop sleeps. If your AWS account cannot issue such a pass (AWS SSO cannot), your computer signs each connection instead, exactly as before, and needs to stay reachable for the meeting.
Fixes
- Reconnecting to a task no longer loses your credentials. Pressing Reconnect re-launches the assistant, and it was being launched without the credential the original run had - so a task with a perfectly good workspace could not reach a model.
- The Foxl tile in Settings > Appearance > Color theme showed a blue dot for a palette whose buttons are black. Every other palette's tile paints the colour that palette's buttons and active states use, so Emerald's dot is that green and Dracula's is that purple. Foxl's tile took the brand blue instead, which the theme reserves for selection outlines - so the one tile you would use to go back to Foxl was the only one not describing itself. It now shows the same black (white in dark mode) the theme actually leads with.
- Your profile photo now reaches the people you message. A photo you picked on the Account page was only ever saved on the device you picked it on, so it showed up in your own sidebar and nowhere else: every message you sent to a team channel still carried your old picture, for you and for everyone in the channel, and your phone and your other computers never saw the new one either. The photo is now saved to your Foxl account, so it appears everywhere your name does - channels, DMs, member lists, mentions and huddle invites - on every device you sign in to. Remove photo now clears it for everyone too, instead of only on the device you press it on.
- On the web app in a browser, Settings > Notifications showed a full set of switches for desktop banners that a browser cannot show. They did nothing at all. That section now says so in one line and points at push and email, which do work there.
- Two entries in the notification test picker, WhatsApp and Web, showed an internal placeholder instead of their names in every language.
- The "Run now" menu in Settings > Heartbeat no longer carries its own two test-feed items. They fired the same notifications the test picker in Settings > Notifications already sends, and that one exercises the full path including the buttons and the link the notification opens.
- The Notifications settings screen no longer offers the phone push list to someone who is signed out, where saving could never have worked, and says to sign in instead.
- Opening notifications from the bell no longer shrinks the screen behind it. The bell sits in the sidebar footer, so on a phone the menu is already open and pushed back when you press it - and the sheet then scaled that whole view down a second time, sidebar included, springing it back on close. The sheet now opens over it flat, the same way Settings already did. Sheets that do leave a screen visible behind them, like the model picker, still lift off it as a card.
- The Sign In page no longer scrolls, and the form is now actually in the middle. Opening Sign In from the sidebar put a scrollbar on the page at every window size, however tall you made the window, and left the form sitting slightly below centre. The page was asking for the full height of the screen while living inside the area below the header, so it always overflowed by exactly the height of the app's own chrome - 62px, at every size measured. It now fills the space it is actually given, so there is no scrollbar unless the form genuinely does not fit, and it is centred in that space.
Changes
- Foxl is free, and nothing in the app or on the site says otherwise any more. Credits, usage meters and upgrade prompts described a balance that cannot be spent: Foxl supplies no model inference, so those credits had nothing to buy. Gone from the sidebar (the "12% used" bar and the Upgrade link), from Account (Credits, the per-model usage breakdown, the storage cost) and from Foxl Code's own account screen (credit balance, compute and storage totals, the per-model chart and the ledger). What is left on Account is your profile, which is what an account screen is for.
- The website no longer advertises plans it will not sell. The pricing section on the Foxl Code page is gone rather than trimmed to a single free column, the Pricing and Refunds links have left the footer, and the credit price table has left the pricing page - it quoted a per-message price for messages that cannot be sent. The Terms now have a short "Price" section saying Foxl is free instead of a subscriptions-and-payment one, the Refund policy says there is nothing to refund, and the privacy policy no longer lists a payment company among the services your data reaches, because it does not reach one. Both pages stay where they were, so any link to them still works.
- The collapsed sidebar is back to the shape it had in v0.6.2. Pressing Cmd+B leaves the narrow bar showing the pages of the product you are in, with your chats and tasks along with them as their first letters; Notes and Code are not offered there, as before. Widening the sidebar again is one click on it.
Features
- The Agent / Notes / Code switcher is back at the top of the sidebar. This release had replaced it with three rows in the navigation, one per product. The idea was that you could always see all three - but the two you are NOT using then sat above the pages and the list of the one you are, and on a phone, where the drawer is the whole screen, that is the most expensive space there is: in Foxl Notes, Agent came between the Notes row and your own recordings. Putting the current product's row first was tried and moved the cost around rather than removing it. The segmented toggle carries the same three destinations in the height of one row, so it is back, and the sidebar below it is your product's pages again. All three names are still on screen at all times, and the collapsed icon rail still lists the products since the toggle is hidden at that width.
- You can now get Foxl updates a release early, under Settings > Updates > Beta updates. Turn it on and the desktop app starts offering beta builds as soon as they are put out, before they go to everyone. A beta is the same Foxl signed the same way, using your normal account and your normal data - it is not a separate environment or a test server, only the app itself is ahead. When the matching stable release ships you move onto it automatically, so you are never left behind on an old beta. Turning the setting back off keeps you on the build you are running until that stable release arrives rather than putting you backwards onto an older one. Beta builds are marked as prereleases, so leaving the setting off means you will never be offered one, and the download links on our site always point at the stable release.
- Every reply now carries a receipt. Hover an assistant message and its timestamp appears; open it and you get the model that answered, the tokens it read and wrote, what the prompt cache saved, how full the context window is, and the estimated cost of that turn. It is the whole turn rather than the last model call, so a reply that ran six tools reports what the reply cost. There is no new button in the message footer: the timestamp is the door, so the transcript gains a fact and no extra chrome. Rows recorded before this update show the model and the time and simply omit what was never measured.
- The status HUD in the titlebar can now keep its numbers on screen instead of behind a click. Clicking it still opens the same panel it always did - tokens in and out, how much of the prompt was cached, estimated cost, context used, which MCP servers are connected, which tools have been called - and that stays the default. What is new is a switch in the corner of that panel: turn it on and those readouts move into the bar itself as one segmented pill, updating live while an agent works, so watching a long run no longer means holding a dropdown open. The status dot at its left folds the readouts away and brings them back when the window gets tight, and the chevron beside them opens the full panel exactly as before, so nothing becomes unreachable in either shape. CPU, memory and disk are deliberately not there: those describe the machine rather than the run, and the System page already answers them.
- You can choose which key sends a message. Settings > Appearance > Send messages with: Enter (Shift+Enter for a new line, the behaviour Foxl has always had) or Cmd+Enter, which turns Enter back into a plain new line for anyone who writes long prompts. Command+Enter sends in both modes. The choice is remembered per device, because the right answer depends on the keyboard in front of you.
- Pin the chats you live in. Any conversation's context menu now has Pin, and pinned chats sit in their own group at the top of the sidebar instead of sliding down the list every time you answer something else. Up to eight, per device.
- Open a second conversation beside the one you are in. "Open beside" in a chat's context menu docks it as a resizable pane on the right, so you can read one thread while writing in another instead of bouncing through the sidebar and losing both scroll positions. The pane is read-only for now, and "Open here" swaps it into the main pane when you want to answer in it. Desktop widths only, and the width is remembered.
- File paths in a tool's arguments are clickable. A path in an expanded tool row (what
file_writejust changed, whatfile_readlooked at) opens the workspace viewer at that file, and at the cited line when the tool named one. Backticked paths in the model's prose have been Cmd-clickable for a while; the ones in the rows underneath were the last dead ones on screen. - A scheduled heartbeat can wait for something to happen.
HEARTBEAT.mdtakes an optionalwatchblock with a shell command: Foxl runs that cheap command on the interval and only wakes the agent when its output or exit code CHANGED, then hands the agent that output so the run starts knowing what it is reacting to. A 24/7 agent stops paying for a model turn every interval to discover that nothing happened, and the interval can get shorter rather than longer. Off unless you configure it. - Schedules ships with four pre-made jobs, and every one of them is switched off. A morning briefing on weekdays at 8:30, an evening wrap-up at 6:00, a Friday weekly review, and a Sunday pass over what Foxl remembers about you. They are there so the page shows what the agent can do on a schedule instead of an empty list, and nothing runs until you turn one on - Foxl does not start doing work on your machine because you installed it. They are otherwise completely ordinary schedules: edit the prompt, change the time, Run now, delete. Deleting one is permanent - it does not come back on the next launch, which is the part that decides whether a shipped row is furniture or yours. And when you want it back, "Start from a pre-made schedule" appears under the list with exactly the ones you do not currently have; picking a card opens the normal form already filled in, so you can read the prompt and adjust the time before you save it. An install that already has schedules of its own gets nothing added to it by this update.
- Grok 4.6 is selectable everywhere Foxl runs a model. xAI's frontier model for coding, agentic work and knowledge work is now in the picker on the desktop app, app.foxl.ai, the phone apps, Foxl Notes and Foxl Code - on your Foxl credits, with no xAI key, and also on the desktop with your own AWS account. It brings a 500K-token context window and four reasoning depths (low, medium, high, xhigh); reasoning is always on, so turning thinking up asks it to think longer rather than switching it on. It lists at $2.20 per million input tokens and $6.60 per million output, which puts it between the fast and flagship GPT tiers, and it is available on Pro and above.
- A file Foxl writes for you arrives whole, or not at all. Until now the agent had no way to write a file except by handing it to the shell, and the shell reads the contents: a dollar amount like
$0.35, anything in backticks, or an&in the filename could be taken as an instruction instead of text, and a long document could stop halfway and still be reported as saved, because a half-written file looks exactly like a finished one from the outside. Foxl now writes files directly. The contents are passed as data, so nothing in them is interpreted; a replacement is swapped in as one step, so an interrupted save leaves your existing file untouched instead of half-overwritten; and after writing, Foxl re-reads the file and reports its real size and checksum, so it can tell you a file is saved only when it has actually seen it on disk. Long documents are written in pieces and joined as they go, which is what makes a 25 KB note reliable.
Changes
- "How much may Foxl do on its own" is one card in one place now. It used to be two, on two screens: Settings > Autonomy held the switches (start agents without asking, find its own work, merge its own green PRs) while the Schedules page held the clock (how often it checks, how often it looks for new work, quiet hours) - and each screen carried a paragraph telling you the other half was somewhere else. They are the same question, so they are the same card: Autonomy, on the Schedules page in Foxl Code, under the schedules it governs. Every switch, every timer and the loop's own health line are still there, and each interval now sits directly under the switch it paces instead of being a number with no subject - the check interval and quiet hours under "wake up on a timer", the survey cadence under "find work when the queue is empty". One Save button, and Settings no longer has an Autonomy pane. The daily spend cap stays in Settings under Defaults & budget, beside the per-task ceiling, because money is not timing.
- Foxl is free, and it now runs on your own model access rather than ours. The Pro and Ultra plans are no longer offered, and foxl.ai no longer serves model inference - so Foxl asks you to bring a provider key, point it at a local model, or sign in with a Claude or ChatGPT subscription you already pay for. That last option is the one worth knowing about: if you already have Claude Pro/Max or ChatGPT Plus/Pro, Foxl adds no bill at all. Everything else the service does is unchanged - accounts, cross-device sync, the desktop tunnel, Channels, notifications and shared folders all keep working exactly as before. If a request does reach our model endpoint it is declined with a message saying which of the three options to pick, rather than failing silently or signing you out. This is a switch rather than a removal: nothing was deleted, and the plans and hosted models can be turned back on without you reinstalling anything.
- Work's "Needs you" tab is one list now, in priority order, and every card can be dismissed. It was three lists stacked on top of each other, one per source, and it looked and behaved like it: three different card sizes in the same column, three ways of printing a repository (
acme-webon one card,foxl-ai/acme-webon the next), three ways of printing an age (waiting 4d,26d ago,idle 185h), and buttons that were stretched to fill one card and label-width on the one below it. Because the blocks were never sorted against each other, where a card landed depended on which list it came from rather than on how much it mattered: hard-capped runs from 26 to 91 days ago sat above pull requests that were approved and one press from shipping. Now every row is ranked on one scale, most blocking first: a question holding up an agent, then a plan waiting for your go-ahead, then finished work you only have to merge, then work Foxl can be sent back to do, and post-mortems last. Oldest first within a tier, so the thing you forgot floats up. - Dismissing works everywhere, with one press. The red-build rows could not be cleared at all, so they sat there for days with no control that would remove them. Suggestions could, but it took three interactions through a menu and a text field. Every card now has an X in the same corner (44px on a phone), and hiding one hides the whole subject rather than leaving a sibling row behind. Explaining why is still offered, under the card's own menu, because that is what stops Foxl proposing the same thing next week. A red build you hide comes back if it fails again: the dismissal is per failure, not a permanent mute. The one card with no X is a plan waiting for approval, because there is nowhere else in the app to find it.
- The same piece of work no longer appears twice, or contradicts itself. One failing build could produce two cards with two "Fix it" buttons, and one task could show "Approved and waiting on you to merge" directly above "This run has shown no sign of life", with a Merge on one and a Retry on the other. Everything about one pull request or one run is a single card now, and a second thing worth knowing about it appears as a quieter line underneath instead of as a card that argues with the first. A run that finished and is waiting on you is also no longer reported as dead.
- The repo filter now narrows the whole tab. It used to skip part of the list, so picking a repository still left other repositories' rows on screen and the control read as broken. Rows that belong to no repository still always show.
- Switching product keeps your place, and Settings is one row. Going back to a product returns you to the page you were last on in it, instead of resetting you to its front page: Files, then Notes, then Agent now ends up back at Files. Cmd+1 / Cmd+2 / Cmd+3 jump between the three and restore your place too. There is one Settings row instead of a different one per product. And clicking Notes when you are already in Notes opens a fresh empty note, the same as Cmd+N - Agent and Code have their own Chat and Tasks rows for that, and Notes did not.
- Nothing in the sidebar is tinted to show where you are. The current product was briefly painted in the accent blue; the switcher marks it as a raised tab instead, and a page you are on is marked more quietly than the headings above it. The one coloured highlight in a list should be the thing you have selected, not the heading above it.
- The Files view's top row on a phone is one Refresh button now, not three. v0.6.6 turned those three icons into glass chips, and two of them did not belong on a phone at all: open-folder reveals the workspace directory in a desktop file manager, which a phone does not have, so it was a control that could only ever do nothing, and search duplicated scrolling a list that is already the whole screen. Refresh keeps the 44px chip that matches the sidebar toggle beside it. The desktop toolbar still has all three buttons, unchanged.
- The narrow icon sidebar is legible again. Collapsed, it now shows the three products at the top, a divider, and then the pages of whichever product you are in - all in one centred column, with the one you are in marked. Before, the product icons and the page icons sat in two columns eight pixels apart, and your conversation list came along as a stack of single letters (one per chat title) between them, which pushed the icons that mean something off the bottom. Lists do not appear in the narrow bar at all now; the row above each one is its door.
- The phone's bottom bar is one row for the whole app and no longer rearranges itself. It used to be a separate row per product, which meant tapping a tab could replace the row it was in, and on Foxl Notes there was no bottom bar at all - the app's main navigation simply vanished on one of its three products. The shipped row is now Chat, Notes, Tasks and Channels: each product plus your workspace conversations, one tap from anywhere. It is still yours to change under Settings > Bottom bar - any destination, any order, up to five, labels on or off - and if you had already customised a row, that row is what you keep.
- The cookie notice on our websites is live, because Google Analytics is now actually configured. The consent-gated tag has been in the code for a while with no property behind it, which meant the notice never appeared and nothing was ever counted. Now foxl.ai, docs.foxl.ai, app.foxl.ai and code.foxl.ai ask once, on your first visit, and the answer is remembered for every one of them - one cookie on the parent domain, so you are not asked four times. Decline and nothing is loaded: no gtag.js request, no analytics cookie, no page view. Accept and we count page views only. "Cookie settings" in the foxl.ai footer takes the choice back at any time, and withdrawing stops collection in the tab you are already in rather than just clearing a cookie. The Foxl desktop app and the iOS and Android apps contain no analytics and send nothing, which is why they never show the notice - a hostname allowlist keeps the tag on the four websites and nowhere else.
- Settings > Tab bar is no longer shown in the desktop app. The row of tabs it arranges lives at the bottom of a phone screen, and the desktop window cannot be made narrow enough to draw one - so the pane sat there editing chrome that build never shows, and desktop search offered its rows as though they led somewhere. Both are gone on the desktop. Nothing changed on the phone or in the browser: the pane is still there, still per-device, and still says that what you set is saved on this device and takes effect at phone width. No layout you have already arranged is touched, on any device.
Fixes
- The second card of the first-run tour pointed at nothing, and now it introduces your own agents. It was about connecting apps, and it was written when Integrations was a row in the sidebar; that moved into Settings a release ago, so the callout had no target left and floated in the empty space beside the list - which reads as a rendering fault rather than as a tour. It now points at Agents, a row that is really there, and says what that screen is for: give a worker its own instructions, its own tools and its own model, then hand it the jobs you would rather not do twice. Connecting apps is still where it was, under Settings.
- Deleting a file no longer leaves the app unable to be clicked. Right-clicking a file in the Files tree, choosing Delete and confirming it deleted the file correctly and then stopped the whole window from responding to the mouse - no dialog on screen, no error, nothing to do but reload. Keyboard still worked, which is what made it read as the app having crashed. Two copies of one internal library had ended up in the build, and each kept its own idea of whether the page was supposed to be clickable: the right-click menu handed the page back, and the confirmation dialog then took it away again on its way out. Now there is one copy, so the lock is released by whoever took it. Two other confirmations opened the same way and are fixed with it - "Restart" on the relay under Mobile/Web, and archiving a shared skill.
- A brief disconnect no longer takes over the screen. Losing the agent connection for a few seconds used to raise a full-screen panel with a blur over whatever you were reading, six seconds in, including for the reconnects that heal themselves a moment later. Now it starts as a small chip under the header with a Retry button, the transcript stays readable and scrollable underneath, and the full panel is kept for an outage that is actually persistent or has stopped healing itself, where it has a reason and a countdown to show you.
- A message typed with no signal is queued instead of lost. On the phone and the web app, sending while offline used to fail the request and turn your text into an error bubble. It now goes into the send queue above the composer, which says it is waiting to reconnect, survives the app being killed, and sends in order the moment the network is back. The queue's existing Remove and Send-now controls work on it.
- Selecting text in a tool row no longer collapses the row. Dragging across a command to copy it ended in a click on the row, which folded it and threw the selection away.
- Copying a reply now pastes the reply and nothing else. Selecting an assistant message and pasting it into a document brought two empty grey boxes along above the text - the message's own Copy and Regenerate buttons, which sit in the same row as the words - plus the "Thought for 4 seconds" and "Used tools" lines and, in a dark theme, the app's own text and background colours. What lands on the clipboard now is the message: its paragraphs, headings, lists, tables, code blocks, links and any image you attached, in your document's own styling.
- A model you picked from one of the third-party providers now runs your agents, not just your chat. Choosing DeepSeek, Groq, Z.ai, Kimi, Qwen, OpenRouter, Together, Mistral, Fireworks, Perplexity, xAI, Cerebras, SambaNova, Cohere, AI21, HuggingFace or MiniMax worked in the chat pane and quietly did not work anywhere else: the Agents page, every schedule and every subagent sent that provider's model name to OpenAI instead, so it failed with an OpenAI error naming a model OpenAI has never had - or, if you had no OpenAI key, with a complaint about a missing OpenAI key for a provider you were not using. All three places that talk to a model share one definition of "which endpoint and which key" now.
- Gemini with your own Google AI Studio key works in chat. It was being sent to Amazon Bedrock, which answered that
gemini-2.5-prois not a valid model - a confusing failure that named the wrong service. Saving a Google key also takes effect immediately instead of on the next restart. - vLLM and LM Studio no longer need an OpenAI key to run a local model. They ask the server on your machine which models it has loaded, so those models appear in the picker; MiniMax does the same with your key. Previously all three could be configured and none of them had anything to select.
- A provider whose key you deleted now says so instead of borrowing your OpenAI key. Removing a key while that provider was selected left the endpoint in place, and the next message went to that vendor carrying your OpenAI credential.
- Picking Gemini CLI for a scheduled or background run now explains why it cannot do that, rather than failing with an unrelated Bedrock error. It runs as its own command-line process with its own tools, so Foxl's tools are not available to it; chat is unaffected.
- Tapping Settings in Foxl Code or Foxl Notes on a phone now opens the settings list, not one section buried inside it. Both products dropped you straight into a single pane - Autonomy for Code, Recording for Notes - so the fifteen other sections were one unmarked back-press away and nothing on the screen said a list existed. The phone now lands on the grouped list, the same place Settings opens from the Agent product. A link or a command-palette pick that names a section still goes straight to it, and the desktop two-pane view is unchanged, since its section rail is on screen either way.
- Runtimes opens as a list now, and both layouts show 12 at a time. The page used to open as a grid of cards, which is a pleasant shape for three runtimes and a lot of scrolling for thirty: a runtime is a row of facts - status, repo, model, branch, spend - and those line up down a column. The card grid is still there behind the switch in the section header, and the switch still remembers which one you picked, so if you had already chosen cards you keep cards. Both layouts are paged now, twelve to a page, with the range and a Prev/Next pair under the list; the count beside "Active runtimes" still reports everything that is running, not just the page you are looking at. Flipping between list and cards keeps you on the same twelve rather than scrolling the window out from under you, and a runtime finishing while you sit on the last page moves you back a page instead of leaving you on an empty one.
- A shell command the agent runs gets three minutes instead of one, and one that is waiting for input no longer waits at all. Anything that legitimately takes longer than a minute - searching a whole home folder, cloning a repository - used to be cut off at sixty seconds and reported back as a failure, and because the command was killed partway anything it was writing was left half-finished. The agent can still ask for a longer limit when it knows the job is long. Separately, a command that stopped to read input nobody was going to type used to sit there for the entire limit before being killed; it now ends immediately with the shell's own error, so the agent sees what was actually wrong instead of a timeout.
Features
- Channels is now one screen for every conversation, including the ones that come from Slack, Telegram and Discord. Connecting a bot under Settings > Integrations > Channels already worked - somebody messages it, Foxl answers with your local tools, the reply posts back to that platform - but there was nowhere in the app that showed a word of it. The transcript was filed as an ordinary chat, so a stranger's message landed in your own AI-assistant sidebar between your private chats, with
[telegram/alice]inlined into every line and no indication of which platform the answer went out on. Channels now lists those conversations in their own group beside your team's channels and DMs: who wrote, what they said, what Foxl replied - and each reply is labelled, so an answer the AI wrote can never be mistaken for one you sent. You can also reply yourself, straight from that thread; it goes out on the channel it came from, in the right Slack thread, and it is only recorded once the platform has actually accepted it. Tapping a "message from Telegram" notification lands on the thread instead of on the assistant chat, and the Channels row in Settings links to the conversations it has collected. Threads live on the desktop, so they show up wherever your desktop is reachable - including a phone on the relay - and they no longer need a team: a user in no shared workspace can read and answer them. - Every subagent is now one of the ones you can edit, and a new switch decides whether it starts from the conversation or from a clean slate. Delegation had two kinds of worker and only one of them was on the Subagents screen: when the main agent named a type it ran the definition you wrote, and when it named none it ran a hidden generic worker that no file described, so editing
general-purposechanged nothing about the case that came up most. There is one kind now. A spawn that names no type runsgeneral-purpose, so the prompt, the toolset and the model you set there are the ones that actually run. The new Inherit conversation context switch is the setting that made that possible. On, the subagent starts already knowing the conversation and shares its cached prompt, so handing it work is much cheaper and you do not have to brief it from scratch - the main agent is told which subagents already have the context so it stops re-explaining. Off, it starts fresh from the task alone and uses its own prompt, its own narrower toolset and its own model. The trade is real and the editor says so in plain terms wherever it applies: a subagent cannot both inherit the conversation and hold a reduced toolset, because sharing the cache means running on the same tools.general-purposeships with it on, which is exactly how untyped delegation already behaved, so nothing gets more expensive; the read-onlyresearcherandcode-reviewership with it off, so their restrictions still bite. An existing install keeps whatever you have chosen.
Fixes
- The mark on the phone's first chat screen stopped flying off the top when the keyboard opens. Tapping the message field is meant to lift the screen onto the keyboard, and it does - but the Foxl mark above "What can I help you with today?" travelled twice as far as it needed to: it left the top of the screen entirely during the animation and then dropped back down into place at the end. The greeting is centred in the space above the composer, so when the keyboard takes that space it only has to move half as far, and it now animates exactly that far and stops. The same screen in Foxl Code gets the same fix.
- Foxl Code's Working and Done lists stopped updating whenever everything was working. The Work page's task stream skips its background refresh while the live connection is up, on the grounds that the connection announces every change - but it was never subscribed to those announcements. So on the healthy path it had no source at all: a task moving to review, a pull request merging, a run finishing did not appear until the connection dropped, you navigated away and back, or you reloaded. It listens now. Only the good path was affected, which is why it went unnoticed: losing the connection made the list start working again.
- Swiping in from the left edge on a phone opens the sidebar again. v0.6.6 gave that edge to an interactive swipe-back, so the gesture people had been using to reach their chats started navigating backwards instead. The two cannot share it: both are the same drag from the same 24 pixels, and the back gesture engaged first every time, so the drawer's swipe silently stopped existing. The edge belongs to the sidebar. Back is still a tap on the Back chevron in the headers that draw one, and on Android the system's own back gesture is unchanged.
- Opening the app no longer accuses your desktop of being offline before it has asked. Every cold open on the phone put up the orange "Desktop offline" warning and then withdrew it a moment later, because the app started from "offline" and had no way to say "still looking" - so a desktop that was up the whole time was reported as away on every single launch. The app now says nothing about the connection for the first couple of seconds and only reports it as offline once that is actually the answer; a desktop that answers in time goes straight to connected with no warning in between. The connection attempt itself starts the instant the app opens, exactly as before, so nothing reconnects any slower. The connection dot in the sidebar follows the same rule and says "Connecting..." for that window instead of showing a state it has not established.
- The sidebar's nav icons are back when the sidebar is open, and on the phone. They had been showing only on the collapsed icon rail, so an expanded sidebar was a column of plain labels - and since the phone drawer is never collapsed, iOS and Android never showed them at all. The Settings row and the account row had kept theirs the whole time, which made the top of the sidebar disagree with its own bottom. Every nav row now carries its icon in all three states: expanded, collapsed to the rail, and in the mobile drawer.
- "Needs you" and the home summary now count the same thing, and every row in it can actually be acted on. The two numbers were computed from different data: the summary line asked whether a task had an open QUESTION, so it read "0 awaiting input" while the rail beside it read "Needs you 3" - the three being red builds, which that definition cannot see. There is one definition now, rendered by the same function in both places and read from one shared snapshot, and one name for it in each language. Two of those three rows also had no button at all: the "Fix it" verb was gated on a task still being in review, which stopped being the right test when the action learned to reconnect to a settled agent, so a task Foxl could have picked straight back up looked like a dead end. And the third row should not have been there - its pull request had already merged, because the code that clears a notification when a PR closes was skipped whenever the owning task had gone terminal. Merged and closed pull requests now drop out of the queue on their own, so the ones already stuck clear themselves.
- Changing your profile picture on the Account page works, and says so when it cannot. Clicking your photo opened the file picker, and then nothing happened - no new picture, no message, no explanation. Two things caused that: ordinary photos were refused before anything tried to read them (anything over 2MB, which is most pictures straight off a phone, plus several formats the pickers on iOS and Android hand over), and the one place the refusal was written to the screen only existed inside the profile editor, so a photo picked from the picture itself failed in total silence. Photos up to 25MB are accepted now - they are scaled down on your device either way - and every attempt says what happened, whether it worked or not. The picture in the sidebar follows the change straight away instead of waiting for a reload.
Features
- The Files view has a proper header, an always-visible search, and a real empty state. The file tree used to start cold - no title, and the surface's only name was a word the desktop chrome tucked into a corner and a phone hid entirely, so on a phone the page had no title anywhere. Files now opens with a title and a line saying what it holds, the same shape the rest of the app's pages use. Search is no longer hidden behind an icon that cost a click and hid the fact the tree could be filtered at all - the field is always there at the top of the list, with its result count inside it. And an open pane with nothing selected now shows a centred prompt to pick a file rather than a stray label. Both the Foxl Agent and Foxl Code file views get the same treatment.
- A Foxl Code schedule can now ask you what to do, or file the issues itself, instead of only opening pull requests. Every schedule used to end the same way: it started a coding agent, which changed the code and pushed a branch. So the two schedules people most want could not be written at all. "Every morning, look at the repo and tell me what needs doing" became an agent pushing a branch at 09:00, and "keep my backlog stocked" opened the work before anyone agreed it was worth doing. A schedule now picks what each run produces: a pull request as before, a question listing the action items it found, or GitHub issues with the reasoning behind each one. The two new kinds change no code at all, and that is enforced by where they run rather than by asking nicely in the prompt - they never get a checkout, so there is nothing to push. A question arrives the same way every other Foxl Code question does, so it reaches a locked phone and can be answered from there; issues count against the same open-issue budget the autonomous loop respects, so two features cannot quietly fill your tracker together.
- The Foxl Code home screen now shows the state of every task, on one screen, above the composer. Four screens each answered part of "what is happening": the sidebar list is short and grouped by day, the right rail lists decisions waiting on you, Inbox is a feed of events, and Work is one repository at a time. None of them answered which task is blocked on you right now. The home screen now opens with a board: how many are waiting on your answer, how many are working, how many are done, and then the rows themselves - grouped so anything blocked is at the top. Each row names the task, what came of it (merged, awaiting review, checks failed and which check), its pull request number and how long the run took. A task stopped on a question used to look exactly like one still running, everywhere in the app; it no longer does.
- Foxl Code's Work page is three plain answers now, not a mirror of GitHub. It used to be four tabs of raw issues and pull requests, one repository at a time, in developer language - and it left five things unanswered: the scope changed silently between tabs (some cross-repo, some tied to one repo), nothing could be dismissed, it was unclear what you were meant to do, "CI red" meant nothing to a non-developer, and only twenty merged items ever showed. Work is now Needs you / Working / Done, across every repository by default, with one filter chip to narrow to a single repo. Needs you leads with the decisions waiting on a person, each with the one button that settles it; Working is what your agents are doing right now; Done is everything that shipped and pages back through your whole history rather than stopping at twenty. The status words are plain - "Checks failed", "Ready for your review", "Agent is working"
- and every row can be dismissed or deleted.
- iOS gets the native gestures it was missing. Swipe in from the left edge to go back, and the screen follows your finger with the one underneath sliding in behind it - the same interactive back every iOS app has. Sheets that leave the screen visible behind them now lift off a stack the way the system's do, that screen settling back as a card, and they respond to a flick by where it was headed rather than to how far it happened to travel. Pull a list down past its top to refresh it, and tap the status bar to jump back to the top.
- Long-press a conversation on iPhone and the real iOS menu appears - the row lifts, the rest of the screen blurs, and Rename and Delete arrive with a haptic. The long-press did nothing before on a phone.
- Text follows the iOS text-size setting and the system typeface. Foxl was the one app on the phone that did not move when you changed Display text size; now every screen does, and body text is San Francisco with the platform's own spacing.
- A destructive confirm on iPhone is a proper action sheet - the dangerous choice in red at the bottom edge under your thumb, with Cancel set apart - instead of a dialog whose two buttons looked the same.
- Report an issue, from Settings. Settings > General now carries a Report an issue row. It copies the build you are running, your platform and your language, then opens the Foxl Discord so you can paste them with what went wrong - reports arrived without that line before, and asking for it is a round trip before anyone can start looking. File paths are deliberately left out: the data directory and workspace both contain your home directory, and this text is written to be pasted into a public channel. The row is also findable from the command palette by typing "report" in any of the ten languages, along with eight other rows in General that search could not reach before (language, launch at login, keep running when closed, show in menu bar and the rest).
- Subagents are now something you write, not one fixed kind of worker. When Foxl delegated a piece of work it always spawned the same thing: the same instructions and every tool the app had. The Agents screen has a Subagents tab where each one is a named prompt plus exactly the tools it is allowed to use, and optionally its own model - so a reviewer can be given read access and no shell, and a researcher can be stopped from writing anything at all. The main agent sees the list with your description of each and picks by name when it delegates. Three are there to start with (general-purpose, researcher, code-reviewer); you can edit those, duplicate them, or write your own, and a "restore" button brings back any starter you deleted without touching the ones you changed. Definitions are plain JSON files under
~/.foxl/agents/, so they can be hand-edited and shared. The run history that used to be this whole screen is still there on its second tab, and the screen remembers which one you were on. - A subagent can only ever be given LESS than the app allows. The per-tool switches on the Tools page stay the final word: a definition narrows what an agent may do and can never widen it, so turning a tool off there switches it off for every subagent regardless of what any definition asks for. A definition that names a tool which is currently unavailable - switched off, or waiting on a skill or dependency - says so in the editor and keeps the setting, rather than quietly dropping it.
- The subagent editor is built for a phone, not shrunk onto one. It is the same shape Schedules uses, for the same reasons: the roster is a table on a wide screen and cards on a phone (a five-column table at 390px is a sideways scroll), the editor opens as a panel that says which agent it is editing instead of unrolling under the list, and the prompt and the toolset are two tabs rather than one very long column - so on a phone the tools you came to change are on screen when you arrive. Opening the editor no longer throws the keyboard up over the form before you have read it, every control including the tool chips is a full-size tap target, and when the keyboard IS up the panel gives it that space, so Save stays reachable instead of sitting underneath the keys.
- A first run that asks two questions and then shows you around. A new install opened straight into an empty chat, which is the least informative screen the app has: nothing said what it could do, and the two things worth setting on day one (how it looks, and how much technical detail you want) were several clicks into Settings. Foxl now opens with a short setup over the live app - pick a light, dark or system mode and one of eighteen colour palettes, then optionally say what you do and how technical an answer you like - followed by three cards pointing at Schedules, Apps and Chat. Nothing is required: Continue works with nothing selected, "Skip all" and Escape leave at any point, and the palette applies as you tap it so you are choosing against the real app rather than a preview. An install you have already used is never shown it, and Settings > Appearance has a "Show setup again" button for anyone who skipped past it.
- Eighteen colour themes. Emerald, Monokai, Solarized, Amber, Dracula, Nord, Rose Pine, Catppuccin, Tokyo Night, Gruvbox, Ice, AMOLED, Kiro, IntelliJ, High Contrast, Everforest, AMOLED Midnight and AMOLED Grey Calm, in Settings > Appearance as well as in first run. Each is a full palette - the canvas, cards, text and the accent - and each has its own light and dark rendering, so it composes with the light/dark switch rather than replacing it. Every tile in the picker paints that theme's real colours, and all 396 text-on-surface pairings across the set were measured to clear the WCAG AA contrast floor. Not choosing one leaves Foxl looking exactly as it always has.
- iOS and Android get the setup flow as a sheet, not a shrunken desktop dialog. It rises from the bottom edge, the palette grid scrolls while Continue stays pinned above the home indicator, every control clears the 44pt touch floor, and each selection has a haptic. The tour docks as a bottom sheet there instead of pointing at a sidebar that is a closed drawer on a phone, and the iOS tab bar stands down while setup is on screen rather than floating across it.
- Integrations is back in the sidebar, as your connected apps. It had moved into Settings, which filed "the services your agent can reach" under configuration - so the answer to what Foxl can do for you right now was three clicks deep, next to the language picker. There is now an Apps section under Agents listing what is actually connected, with Explore beside it for everything else. A fresh install sees a "Connect your first app" row rather than an empty heading.
- Foxl now connects to MCP servers on the new 2026-07-28 protocol, and still to every older one. That revision removed the connection handshake and the session header entirely, so servers built on it are a genuinely different shape on the wire and Foxl could not talk to them at all - it only ever spoke the March 2025 version. It now asks each server which revisions it supports and uses the newest one both sides know, falling back to the old handshake for the servers you already have configured. Nothing to change in your settings either way. Tools -> the server's row shows which revision was agreed, so a server on the new stateless protocol is visibly different from one on an older one.
Improvements
- The first screen on the phone is a centred welcome instead of a left-aligned list. Opening Foxl on iOS or the web app on a phone showed a greeting, a small label and three full-width rows with an arrow on each, all flush left - four different left edges and one ragged right one, which read as unfinished. It is now the Foxl mark, one large question, and the starter prompts as tappable chips that centre themselves, so the eye lands on the choice rather than reading two labels first. The chips are 44px tall, and the whole screen fits without scrolling in all ten languages.
- The phone's home screen only mentions your desktop when something is wrong. It used to carry a permanent grey line reading "Ready" whenever the desktop was reachable, which spent the most prominent row under the greeting on a fact that is only interesting when it is bad. Connected now says nothing at all, and an unreachable desktop is called out as a warning instead of a grey aside.
- One Foxl logo everywhere. The sidebar used a lightened mark on dark backgrounds and a near-black one on light; it and the phone's home screen now both show the original black logo, in both themes.
- Runtimes can be a list instead of cards. The page only ever drew a card grid: three columns of a status pill, a repo name, a model and a budget bar, which is one screenful for four sessions and a lot of scrolling for twenty. A switch beside the "Active runtimes" count now folds the same data into one row each, with the spend figures in a straight column down the page so the runtime about to exhaust its budget can be found by scanning rather than by reading every card. Cards stay the default and the choice is remembered per browser, separately from the one on Repos. Either layout opens the session on click and carries the same right-click menu and the same actions button, so Stop runtime is one tap away in both.
- The Files view's top-right controls are glass on the phone, like the rest of the header. Search, open-folder and refresh sat at the top of the file list as three small flat icons while the sidebar toggle beside them was already a rounded glass chip - the one row where the mobile header was left half-finished. They are now 44px chips that match it: real iOS Liquid Glass that lenses the file list scrolling underneath on iOS 26, and the same frosted CSS chip on Android and the web app. The desktop toolbar is unchanged.
- Foxl Code's home screen is a chat home again: the mark, one question, and a single line of counts. It had grown into a stack of four blocks under the greeting - a board listing up to 24 tasks grouped by who was blocked, a footer saying how many more were not shown, and a collapsible panel narrating what the autonomous loop had been doing. On a phone that stack is centred in the space above the message bar, so a list that tall pushed the logo and the heading clean off the top of the screen: the app opened on a wall of task rows with nowhere for the eye to start. What survives is the one thing that reads at a glance, "0 awaiting input · 4 working · 77 done", directly under the question, so the screen matches the Foxl Agent home. Nothing became unreachable: every task still shows its own state ("Running", "Checks failed", "Review approved") in the sidebar one tap away, and the loop's activity feed still appears above the conversation as soon as the home thread has a message in it.
Changes
- Foxl Code has one "Needs you" list now, not four. Work and Inbox sat next to each other in the sidebar and both opened on a tab called "Needs you" - and neither was the whole answer, because they read different tables computed from the same GitHub state. So one green pull request could show up three times at once, on two screens, offering three separate Merge buttons, and clearing one list left the others full. Inbox is gone as a page: its feed of everything the agents did is now Work's Activity tab, where it reads as history instead of competing to be the decision list. Work's own first tab is the single list of decisions waiting on you, with duplicates about the same pull request folded into one row that keeps the richest version of it - the reason it is waiting, and Dismiss and Snooze. Budget warnings and hard-capped agents still surface there, since nothing else reports them. The desktop side rail no longer repeats that list beside itself on the Work page; it stays on Tasks, where it is the only thing on screen answering the question. Every old link still lands:
/inbox,/code-inboxand/activityall open Work, on the same tab they used to. - Integrations moved back into Settings > Connect. It sits between Workspace and Mobile/Web now, rather than as its own sidebar row, so the sidebar is the shorter list of places you work and the things you connect Foxl to live together under Settings. All six tabs come with it (Integrations, Skills, Shared, Channels, System, Custom), and Skills/Channels stay one click away inside that section.
- Live translation now lives inside the Notes AI features settings. It had its own section in Settings; it sits under AI features now, behind a labelled icon, so every AI-powered Notes option is in one place. The feature itself is unchanged - the same toggle, target language and translation model, just one section over.
- Sidebar rows answer the pointer. Hovering a nav row, a conversation or a recording swells its rounded chip by 1.5% as the highlight fills in, so the row under the cursor reads as the one you are about to click rather than only as a shade of background. It is deliberately small (about three pixels of width on a full-size row) and it runs on the app's standard motion timing, so reduced-motion settings collapse it. Touch screens are left out: there is no hover to end on a phone, so a tapped row would stay enlarged.
- Notifications look like notifications now. An unread one is marked in red - the same red as the count on the bell, because they are counting the same thing; it used to be a blue dot under a red badge, which is two different answers to one question. Every unread count in the app is that one red now, including the badge on the phone's tab bar, which was near-black.
- The notification list dropped its decoration. Each row carried a small coloured square with an icon in it - a sparkle, a clock, a hash - repeating what the row's own first words already said ("Agent Done: ...", "Schedule: ..."), so a list of twelve kinds of notification was a column of twelve coloured squares. A row is now the unread dot, the title, the message and the time, separated by a hairline, and the whole row is what you click. "Read all" and "Clear" are plain words rather than a word with an icon pressed against it.
- The black "Open" button on every notification is gone. It went exactly where clicking the notification already went, so it was the row printed twice - and three solid black pills down a narrow panel were the loudest thing on screen. Buttons now appear only when there is a real decision to make (approve or deny, install an update, join a call), and those are quiet tinted labels. Notifications already in your list lose their duplicate button too.
Fixes
- Tapping a conversation-search result on a phone no longer lands you somewhere else, with the keyboard up. Picking a result opens that conversation with the matches highlighted, and the highlight pass used to make the FIRST match in the whole conversation the current one - so the app arrived at the message you tapped and then slid away to a hit hundreds of lines above it, with the counter reading "1 of 89" for something nobody had asked about. It now starts at the match inside the message the result named, and the landing scroll is instant so the two movements do not fight each other. It also stops focusing the find field on arrival: the query was already typed in the search sheet, so the only thing the focus did was raise the keyboard over the bottom half of the message being jumped to and set the composer animating. On a desktop, where there is no on-screen keyboard, the field still takes focus so Enter keeps walking the matches.
- Sign out from the sidebar now actually signs you out on the phone. Tapping your name at the bottom of the sidebar and choosing Sign out cleared the session and then signed you straight back in, every time, so that door could not sign you out at all. Sign out on the Account screen always worked, which is what made it look like nothing in particular. The phone was replaying the sign-in link it had been opened with: the sidebar's Sign out reloads the app, and the reload handed it that same link again a moment later, credentials and all - so it kept being handed back the account it had just left. A sign-in link is now spent once and never replayed, so both doors sign you out and you stay signed out.
- A custom tool that runs a shell command, evaluates JavaScript or writes a file now asks before each call, like the built-in
exectool always has. The approval gate matches on the tool's NAME and treats a name it has never heard of as allowed, and custom tools are registered under their own names - so none of them matched anything and all of them ran unattended. A custom tool wrapping a shell command was therefore a shell with no per-call prompt, sitting beside anexectool that has one, and it is the model that fills in the arguments. If you have already switched on "auto approve all", or set your own rule for a tool, nothing changes: your rule still wins. Read-only file tools, HTTP tools and MCP tools are unaffected. - A permission rule for a tool whose name contains a hyphen did nothing. Tool names may contain hyphens - a custom tool called
deploy-prodkeeps its name, and every MCP tool is exposed as<server>__<tool>where the right half usually has one (create-issue). The rule compiler did not accept the character, so a rule written WITH an argument pattern (deploy-prod(rm *)) was stored under a name no tool is ever called and never matched again. A "deny" rule in that shape told you something was blocked when it was not. - A custom file tool's blocked-path list now applies to the path it actually opens. It was compared against the path template before it was resolved, so the
~/.sshand~/.awsentries could only ever match a literal~/- which opens nothing - while the real path to an SSH key went straight through./etc/passwdspelled//etc/passwdor reached via..slipped past for the same reason. Paths are resolved first now,~is expanded (so a~/notes.mdtemplate works for the first time), and macOS's/private/etcspelling of the same files is covered. - A Foxl Code task row opens its own menu, not the whole list's as well. Holding (or right-clicking) a task in the Tasks list opened two menus at once, stacked: the row's Copy task id / Cancel / Delete, and the list's New task / Delete all. A row now shows only its own actions; the list's still open from the empty space around the rows.
- Foxl Code updates itself over one live connection instead of asking five times a minute. The task list, the questions waiting on you, plans parked for approval and the suggestion feed each used to be their own repeating request, so the app talked to the server constantly whether anything had happened or not - which is what put twenty thousand requests through a single idle phone in six hours. All four now arrive on one connection that stays quiet until something actually changes, and goes completely silent while the screen is off. Anything running is picked up within ten seconds, and when nothing is running the app stops asking almost entirely. If that connection cannot be made - an older app against a newer server, or a network that blocks it - everything falls back to the old requests, so nothing depends on it working.
- On a phone, each chat in the sidebar no longer sits in its own pale rectangle when a Surface theme is on. Appearance > Surface paints the sidebar as one vertical wash, but every conversation row underneath Chat was painting a flat panel colour of its own, so the rows read as a stack of boxes laid over the gradient instead of sitting in it - most obviously on Dawn, whose wash is both darker and warmer than that flat colour. The row had the fill so it could cover the Rename and Delete buttons it slides away from; those now slide in from the edge with it, so the row needs no colour at all and shows whatever the sidebar is actually painting. Desktop was never affected - it has no swipe row - and it is the look the phone now matches.
- Foxl Code stops calling home while nobody is looking at it. Left open on a phone or in a background tab, the app kept asking the server for your task list and your open questions every five seconds, around the clock - a single idle device sent over twenty thousand requests in six hours and took the account to 90% of its daily request limit with nobody using it. Two things were wrong: the two fastest polls were the only ones in the product that did not stop when the screen was hidden, and the question poll ran twice over because two parts of the screen each asked separately. Both now pause while the app is out of sight and refresh the moment you come back, and the two halves of the queue share one request. Nothing on screen updates any later than before - opening the app, or answering a question, still refreshes it that instant.
- After signing out, the sign-in screen is the whole screen again. Signing out and coming back left the app's chrome behind on the phone and the web: the bottom tab bar floated over the sign-in form and the sidebar button sat in the corner, both offering a signed-out person a way into screens that need an account. The form itself sat below the middle of the screen, because it was centring inside the app's page area instead of the display. Signed out now always lands on the same clean sign-in screen the app opens with, on every route that asks you to sign in.
- The iPhone and Android apps open on sign-in, not on a setup sheet. A new install landed on the two setup screens - pick a look, then a couple of questions about you - before it showed anything the app actually does. On a phone that is a form standing in front of the product, and both things it asks for are already in Settings, one tap away, on a screen you chose to open. The phone apps skip it: sign in, and you are in Foxl. Everything it offered is still under Settings > Appearance. The setup screens stay on the desktop app and on foxl.ai in a browser, where they also explain that a desktop app exists.
- The setup screens no longer put a coloured bar across the top of a narrow window. On a phone-sized screen the brand panel shrank into a solid accent strip pinned above the content, which read like a web page's masthead rather than the app's own setup screen - and it was the only thing on screen that was not part of the question being asked. It is gone below the tablet width; the step counter and the title now start at the top, the way a phone presents setup. The wide two-column layout on a desktop is unchanged.
- A long press on the phone shows one menu, not two, and never a stray one. Pressing and holding a chat in the sidebar popped two menus stacked on top of each other - the app's own rename/copy/export/delete menu, and a second, smaller iOS "rename/delete" lift menu. The same iOS menu also appeared where it made no sense: over the pet, on the Files and Channels rows, even on the sidebar toggle, because it was tracking chat rows that were still there behind the closed drawer. There is now one menu - the full one - anchored to whatever you actually pressed.
- A long press on a sidebar row opens its menu instead of selecting the text. On the chat, Notes and Code lists a press-and-hold used to start highlighting the row's title and drag it out from under your finger. Holding a row now lands on its menu, the way a native app behaves, on both iOS and Android.
- The Agents screen says what went wrong when it can't reach the local server. The Runs and Subagents tabs used to show a bare "Error" with a Retry button; they now name the cause - the agent server is unreachable - and tell you to check that Foxl is running before retrying, in all ten languages.
- The companion counter in Settings counts all 15 characters, not 9. There are fifteen cards in that pane and the number above them said "9/9", so six of your companions were never counted however many you collected - and the same collection read 15/15 in the den and on the summon screen. The commons are most of what a draw gives you, but they have their own cards and you can pick one as your on-screen companion, so they count.
- One button in the corner of the Foxl Code terminal on iOS, not two on top of each other. The sidebar button from the screen underneath kept drawing over the full-screen terminal - a thin ring and its icon, a few points off the terminal's own Back button and taking the tap - so pressing Back could open the sidebar instead. It now stays away for as long as the terminal is up, and the terminal's own Back, keyboard and refresh buttons are the same glass chips the rest of the app uses.
- The message bar now sits on the keyboard instead of floating above it. On iPhone there was a band of empty background between the two - 124px of it - because the bar paid for the home indicator twice over and kept a full tab bar's worth of clearance for a tab bar that is behind the keyboard. On the new-chat screen that surplus was enough to push the greeting up under the header buttons. The bar and the messages above it still travel together, in one smooth move, and with no tab bar on screen the bar simply sits lower.
- A queued message lines up with its own buttons. The text sat noticeably higher than the send-now and remove icons beside it, with a wide band of empty space under it, so each row read as a caption with a pair of buttons floating below. Rows are a third shorter now and everything shares one centre line.
- You can add a message to the queue while a reply is still coming in, on a phone too. Sending mid-answer used to be desktop-only - Enter queues, and a phone has no Enter - so the button was Stop and nothing could reach the queue panel above it. It now queues whenever you have something typed, and sends as one turn when the answer finishes.
- A photo you add to the queue is actually sent. The queue only ever carried the text, while the composer was emptied either way - so attaching an image mid-answer and tapping the button took the image away, showed an empty queued row, and threw it out when the turn ended. The attachments now travel with the queued message, and a message that is nothing but an attachment queues properly instead of vanishing.
- Typing in Korean, Japanese or Chinese mid-answer no longer stops the answer. While the keyboard is still composing a character the app cannot yet see what you have typed, so the button was still Stop - and tapping it cancelled the reply instead of queueing the message. The button now checks what is actually in the box before it does anything.
- A pasted screenshot no longer disappears from a short conversation. Opening a chat you had attached an image to could replace it with "Attachments not loaded (large conversation)" - on a conversation two messages long. A limit meant for transcripts too big to open was being applied per image instead of to the transcript, and an ordinary full-screen screenshot is past it, so the message it showed was not true either. Images are now dropped only when the conversation as a whole really is too large to load, and the newest turns keep theirs. Measured on one real history: of 90 conversations affected, 76 were nowhere near the limit.
- A conversation that IS too large now really does get smaller. Fixing the above left the opposite gap: only an individually enormous attachment counted, so a chat whose weight was spread across a dozen ordinary screenshots was left entirely alone and still failed to open. It is the conversation's total that decides now, and the oldest turns give theirs up first - the ones you just scrolled to keep theirs.
- And the attachments are one click away instead of gone. The notice in their place was a dead end - it said the attachments were not loaded and offered no way to load them, even though the app had always been able to fetch a single message's attachments on request. It now has a button that does, and says so if the fetch cannot reach your desktop rather than quietly showing nothing. Documents come back too: a turn holding a screenshot and a PDF used to return the image, clear the notice, and lose the file with nothing on screen saying so. Still one message at a time and only when you ask.
- An MCP tool can no longer run twice for one request. When a tool took longer to answer than the app was willing to wait, the app assumed the connection had used a stale guess about which version of the protocol the server speaks, reconnected, and sent the same request again - so a tool that sends mail sent it twice, while the agent saw one call and one error. It now only retries when it is certain nothing reached the server, or when the request is a read that cannot have an effect.
- The first-run setup screen no longer has invisible buttons on top of it. On iPhone the app's own frosted controls are drawn by the system, above the web page, so they stayed on screen over the setup flow - painting circles across it and, worse, taking taps meant for the sheet: pressing Skip all opened the menu hidden behind it. Swiping from the left edge during setup also navigated away. The setup screen now tells those controls to stand down, the way every other full-screen panel does.
- The Fix it button's explanation is translated. The tooltip saying that Fix it wakes the same agent, reads the failing check and pushes to the same branch was written in English only, so it appeared in English on an otherwise translated page in eight languages.
- status.foxl.ai no longer republishes the exact words an upstream service used to refuse us. The page prefers a real reason over "check failed", which is right, but those reasons come from services that name internal identifiers in them - so a credentials problem with our model provider would have printed our own cloud account id and user name on a public page, in its JSON feed, in its RSS, and to anyone subscribed to updates. The reason is now scrubbed of identifiers before it is stored, and still says which component failed and how.
- The companion list no longer offers "Follow along". It was one card among sixteen that did not name an animal - it meant "whoever you last brought out", so its picture and its effect depended on state you could not see from the pane. It is gone: every card is a companion, the one currently on screen is the one marked, and drawing a new companion selects it.
- "Show outside the app window" can no longer be turned on when you have no companion. With nothing drawn yet there was no animal to put on the desktop, but the window was still created - a small invisible patch of your screen that took clicks meant for whatever was behind it, with nothing on it to explain itself or to right-click. The switch is now unavailable until you have a companion, and an app that was already left in that state closes the empty window and turns the setting off by itself.
- Shaking the phone no longer asks to undo your typing. iOS treats a physical shake as an undo gesture, so walking with the app open, a car ride or a train put an "Undo Typing" alert on screen with nothing to undo. The iPhone app turns that system gesture off; undo inside the Notes editor is unaffected.
- The phone apps stay portrait instead of rotating into a broken layout. Turning the phone sideways ran the header and content under the notch or camera cutout and the rounded corners, because the app pays for those edges itself and only ever paid the top and bottom ones. Chat, notes and tasks are single-column reading surfaces, so portrait is the shape they were designed for. Fixed on iPhone and Android alike. Tablets are unaffected and still rotate freely, on both iPad and Android.
- On iPhone, the message bar and the conversation now rise together when the keyboard opens. The bar slid up over the keyboard smoothly, but the messages above it did not move at all while that happened - they stayed put for the whole quarter-second and then jumped the full height of the keyboard in a single frame, just after the bar had already come to rest. Closing the keyboard was the same thing backwards, and more obvious: the messages dropped all at once on the first frame while the bar took the rest of the animation to follow them down. Both now travel as one, on the same curve, in both directions. Scrolling back through older messages first is unchanged - the conversation deliberately holds still there, and only the bar moves.
- Typing in a channel no longer pushes the message you are replying to off the screen. Channels and DMs had none of the keyboard handling the other two chat screens have: when the keyboard opened, the room lost that much height while staying scrolled where it was, so the newest message slid out of view and nothing brought it back. The message bar did not follow the keyboard either - it and the whole conversation jumped into place in a single frame once the keyboard had finished opening, and dropped all at once before it on the way out. All of it now moves with the keys, like the Agent and Code screens.
- The tab bar no longer sits between the message bar and the keyboard. On a phone the Chat / Channels / Files capsule rode up with the keyboard and came to rest in the gap above it, where it could not be tapped anyway. It now fades out while you are typing and comes back when the keyboard goes away.
- Notes panels follow the keyboard too. The transcript and the AI panel had the same one-frame jump the chat transcript had, in both directions.
- The search palette and the repository picker fit above the keyboard on iPhone. Both shrink to the space the keyboard leaves - but they were only listening for the signal a mobile web browser sends, which the app itself never sends, so on the phone they kept their full height and their lists ran behind the keyboard with the scrollable part off screen.
- The iPhone tab bar is in your language again. Its labels came out in English on every language the app supports - so on a Korean phone the row along the bottom was the one control still reading "Chat / Channels / Files" while everything above it was translated. Nothing was missing from the translations: the tab bar was asking for them from a place in the app that could not reach them, and quietly showed its English spare instead. It now reads the same translations as the rest of the app, and follows a language change immediately rather than at the next launch.
- The Surface swatches in Settings > Appearance show the right colours again. Each tile is meant to preview its own preset, and instead all of them showed whichever one was currently selected - so picking Dawn made Mist look like Dawn, and on Default, where nothing is tinted, both gradient tiles were blank white boxes with nothing to compare. The tiles now paint their own preset in both light and dark mode, which is the whole point of choosing by colour rather than by name.
- The close button on a pop-up panel sat in the middle of it on iPhone and iPad, inside a circle. It was offset by the height of the status bar, which is the right thing to do for a panel that starts at the top of the screen and meaningless for one centred in the middle - so on something like the "Install Foxl to use Terminal" prompt the X landed a finger's width down from the panel's own top edge, on top of the sentence it was covering. The ring around it was a keyboard focus outline: these panels move focus to their first control when they open, and on a small screen that control is usually the X, so opening one by tapping drew a highlight nobody had asked for. The X is back in the corner where it belongs, the ring now appears only when you are actually navigating by keyboard, and a panel tall enough to fill the screen keeps clear of the status bar and the home indicator so its title and buttons stay reachable.
- A local MCP server with a long tool list is no longer cut short. Servers are allowed to hand their catalog over in pages, and Foxl read only the first one and treated it as the whole list, so tools past that point were missing with nothing reporting it. Every page is read now.
- A skill no longer asks you to install something that is not a program. Each skill lists what it needs, and the Skills page reads that list to tell you what is missing - but it was reading it by a different rule than the Integrations page, so two screens described the same skill differently. The Outlook skill needs a connected Microsoft account, and was reported as needing a missing command called
integration:microsoft. The local text-to-speech skill needs two settings, and was reported as needing two missing commands named after them - which could never be satisfied, because the page looks for a program by that name and a setting is not a program. Both screens now read the list the same way, so what a skill needs is described once. - A value you set for a skill now clears the warning about it. Skills that need an API key or a folder path can be given one from the Integrations page, and Foxl passes it to the skill when it runs - but the "missing" notice beside it only ever checked your shell environment, so it stayed on screen after you had just filled it in. It now counts the value you saved.
- The Integrations page lists the commands a skill can use, where it used to show none. Some skills accept any one of several programs (Code Search takes
rgorgrep, for instance). Those alternatives were dropped when the page built its list, so those skills appeared to require nothing at all. They are listed now. - Repos says "Repos" on a phone again. Its two header buttons kept their full labels on a narrow screen and took the row first, so the page's own title was cut to "Rep..." - the one word on the screen that could not be guessed. Sync and Connect are icon buttons on a phone now, the same as the New button on Schedules, and they keep their labels on a wide screen.
- Foxl Code's Work screen is laid out for a phone. Its four tabs did not fit the width, so "For you" and "Open issues" broke onto a second line and spilled out of the strip they sit in; the tabs now stay on one line, fill the row, and scroll sideways on a narrow phone. The rows waiting on you lined up narrower than everything else on the page and are flush with it now, and their buttons share the row evenly instead of one stretching to the full width beside a small one. Every button on the screen - Merge, Solve, Fix it, Diff, Dismiss and the overflow menus - is a proper thumb-sized target, and an issue's or PR's title gets the width its actions used to take.
Removed
- Channels is a Foxl Agent screen only; Foxl Code's copy of it is gone. One page had two doors, so the sidebar row, the phone tab-bar entry, the unread badge and the
/channelsURL all existed twice - once per product - and had to be kept in step by hand. The Code side is the one that went: the sidebar row under Files, its cell in the Code tab bar (Tasks / Work / Repos is the shipped row now), and/channelson code.foxl.ai, which lands on Tasks like any other unknown path. Anyone who had put Channels in a customised Code tab bar keeps the rest of their layout. Channels itself is unchanged. Same page, same channels, same messages, same unread counts, reached from Foxl Agent beside Chat. Nothing was archived and no conversation moved; on the desktop and iOS apps it is one product switch away. The Integrations setting also called "Channels" - the inbound Slack, Telegram and Discord adapters that let you message the agent from those apps - is a different feature and is untouched.
Features
- foxl.ai says what Pro and Ultra cost. Both plans read "TBA / pricing to be announced" on the pricing page, the homepage and the Foxl Code page, which is the one thing a visitor deciding whether to try it needs. They now show $20 and $200 a month with their credit allowances, and search engines see the same figures in the page's structured data instead of a placeholder. Enterprise is still "talk to us", because it is quoted per deal - the form beside it records the enquiry.
- An open tab now tells you when it is running an older version of the web app. Deploying replaces the files the page loads from, and a tab left open across that does not fail politely - it breaks at whatever moment you open a screen it had not loaded yet, because the missing file comes back as a page instead of code. The app now notices when a newer build is being served (on returning to the tab, and periodically) and offers a reload. It never reloads on its own: anything you have typed stays where it is until you choose to.
- The browser extension now says what it needs instead of failing silently. It talks only to Foxl Desktop on your own machine, so somebody who installs it without the app had nothing to connect to - and the only sign of that was a grey dot whose tooltip said "Disconnected". It now tells you the desktop app is required, links to it, and offers the custom-port setting; if you HAVE had it running before, it says it is reconnecting instead, because those are different problems. That second screen also keeps a quiet way back to the download, and stops claiming an automatic reconnect once a month has passed with no desktop answering - otherwise somebody who uninstalled the app would be promised a reconnection to it forever. The extension and the desktop also exchange versions now, so an extension that has auto-updated past your desktop app says so rather than half-working.
- Foxl Code: the container your tasks run in is yours to configure, whichever coding assistant you use. Settings -> Code -> Container is a new pane with four things that used to be fixed for everyone: whether the agent may edit files and run commands without stopping to ask, how long a task may run before it is told to commit and stop, instructions it reads in every repo, and environment variables for the commands it runs. Nothing here is Claude-specific - the instructions are saved as
CLAUDE.mdfor Claude Code andAGENTS.mdfor Kiro, Codex, Cursor, Hermes and OpenCode, and the one approvals switch becomes whichever flag that CLI has. A repo that ships its own instructions file keeps it, and all four settings apply when you reconnect to a task instead of reverting to the defaults. Environment variables are key/value rows and the time limit is a row of presets, so this is usable on a phone; raw config JSON is still there for one assistant at a time, under Advanced. - The iPhone tab bar is the real iOS one now. It was a web capsule painted to look like glass, because the material iOS 26 actually uses cannot be applied to a bar whose icons and labels are web content. The bar is now the system's own: real Liquid Glass, SF Symbols that fill in when selected, system badges, and the platform's own spacing and tap behaviour. Your tab layout, order and the labels switch carry over exactly as you set them. On Android, on the web and on older iOS nothing changes.
- Text size follows iOS Settings. Foxl ignored Display and Brightness, Text Size entirely, so it was the one app on the phone that stayed put when you moved that slider. It now scales with it. The range is deliberately narrower than iOS allows, because the largest accessibility sizes overlap a screen this dense.
- Tapping the tab you are already on scrolls back to the top, the way it does everywhere else on iOS. It used to buzz and do nothing.
- Confirmations that delete something are real iOS alerts, with the destructive choice in red instead of two buttons that look equally safe.
- The phone tells you what happened. Actions that finish, finish partially, or fail now have the three distinct iOS feedback patterns, so you can tell them apart without looking at the screen.
- Screens have a direction on a phone. Going somewhere new slides in from the right and going back slides in from the left, the way iOS has always signalled which way you moved. Before, every navigation in both directions was the same small upward fade. Unchanged on a desktop, where nothing is being pushed, and unchanged if you have Reduce Motion on.
- Tapping a notification on iOS and Android now opens what the notification was about. A message from a colleague took you to the screen you were last on rather than to that channel: the tap navigated to a path that is the Channels page only on code.foxl.ai, and in the app it resolves to Integrations - which on a phone opens as a settings sheet over whatever was behind it. A channel notification now opens that channel, scrolls to the message it named and flashes it, whether the app was open, in the background, or closed. A Notes notification opens the note that finished instead of leaving the last one you had open on screen, and the notification rows in the bell lead to the same places as the banners.
- Four more notifications that led to the wrong place, or nowhere. A Feed item took you to Schedules, which cannot show it. An "Agent Done" notification dropped the id of the agent it was about, so it opened the list instead of that run. The Foxl Den notification and product-update announcements appeared in the bell with an Open button that did nothing at all. And a Foxl Code task notification opened the Work list from the bell while the banner for the same event opened the task. Every push the server can send is now checked against both doors - the banner and the bell row - so a notification that leads nowhere fails the build instead of shipping.
Fixes
- Foxl Code stops retrying the same issue forever. The autonomous loop is told to retry a failed piece of work once, and that instruction was the only thing stopping it: every retry is a new task with a fresh budget, so a loop that kept deciding to try again could spend one budget after another on a single issue with nothing counting the chain. There is a real ceiling now - one automatic retry, one manual one after that - and once it is reached the loop is told the refusal is final rather than something to wait and re-attempt.
- "Fix it" on a failing build now actually sends the agent back at it. The button asked the agent's machine to pick the task up where it left off, which only works while that machine is still awake - and it goes to sleep after fifteen idle minutes, so a row that had been waiting hours or days could never succeed. Worse, the one thing that was supposed to happen next never did: the "that machine is asleep" answer arrived in a shape the app did not recognise, so instead of waking it up again the button reported an error and stopped. Pressing Fix it now wakes the agent on its own saved workspace - the checkout, its dependencies and its own notes are all still there - and only when that workspace is genuinely gone does it say so and leave Retry to you, rather than quietly starting a paid run from scratch.
- Reconnecting to an agent works, including while it is still busy. Every reconnect this product has ever attempted had failed. Three of them failed for a reason that was not a failure at all: the agent was still working (cancelling a task tells our records immediately but leaves the agent running), and being told "already live" was reported to you as an error instead of simply putting you back in front of it. It now reattaches, and anything you typed goes to the agent that is already there. Two others were attempts on runs that never got as far as a checkout, where the message blamed a Foxl update for clearing a workspace that had never existed; those now say what actually happened.
- A terminal that has gone to sleep says so. Typing into an agent whose machine had idled out returned an internal relay error - the same unhelpful message whether Foxl was broken or your agent had simply been left alone for fifteen minutes. It now reports that the session slept and offers to wake it. The same correction fixes the agent's own status reads, and the message that tells you a code review could not run because a Foxl component has not been rolled out yet - that one had been replaced by a raw error code that read as a problem with your repository.
- Answering the agent's question now gets you an answer back. Foxl Code would ask something, you would reply, and the thread would sit on your own message forever - the reply was written to the autonomous loop's own log, which is shown in one collapsed panel on another page. Replies now land in the conversation you answered in, and they appear there while you are looking at it instead of on the next reload. Two related dead ends are gone with it: a reply no longer waits for the loop's next scheduled beat, and a reply is no longer discarded outright when you have switched the loop off - turning it off means "stop deciding work for me", not "ignore what I typed".
- Questions and notices read as chat. A clarification arrived as an amber warning card and your answer became a grey receipt slab, and the loop's notices were centered amber pills - so the most conversational thing in the product was the only thing that did not look like the conversation. They are ordinary chat turns now: the ask on the left, your answer in a bubble on the right, notices in the same column as any reply. Only a failed send is still coloured.
- The autonomous-activity feed is visible again. It was only rendered on a completely empty home screen, so it disappeared for good once you had sent a single message. It now sits above the conversation at any point, newest first, with a time on every entry.
- You pick the repository the loop works in from a list, and it tells you which one it is using. It was a text box over a rule you could not see: the loop only works in a repository you have already run a task in, so a typo - or a repository you have access to but have never used - was silently refused and it quietly went and worked somewhere else. The dogfooding account sat on a demo repository for days that way. The field now offers exactly the repositories that are allowed, says "Working on owner/name" underneath, and if a saved choice ever stops being usable it tells you that instead of pretending.
- The activity panel says whether the loop has been working, before you open it. Its heading was the words "Autonomous activity" and a timestamp, which tells you the machine is alive and nothing else. It now reads "Worked 4 times in the last day" or "Quiet for a day", so the collapsed panel answers the question by itself.
- The self-directed loop stops quietly parking itself. It was treating pull requests waiting on a human to merge as its own backlog and filing nothing for days on end while reporting success every time. Open PRs are finished work, not a reason to stop; the only limit is its own issue budget. Consecutive empty surveys are now counted, shown in Settings, and after two the loop must either act or say plainly what it is blocked on.
- The desktop app reported its version to the browser extension as "1.0.0", regardless of what was installed - a placeholder that shipped in every release. It now reports the real build, which is also what makes the version notice above possible.
- Cmd+N clears the screen in Foxl Code too. The shortcut (and File > New) reset the URL but left the open task on screen, so it read as a dead key in the one product where the sidebar's + button worked fine. All three doors now run the same "new in this product" action: a fresh conversation in Agent, an empty note in Notes, an empty task composer in Code.
- Opening something by its own URL now scrolls the sidebar to it. Landing on a chat from a link, a reload or a search result highlighted the row in the list without moving the list, and Settings never moved its left rail at all - so the pane you asked for was selected while the rail still showed a different group, which reads as having landed somewhere else. Chat, Settings, the Foxl Code task list and the Notes list all bring the selected row into view now, and leave it alone when it is already visible.
- "Jump to the end" in Notes works on a note nobody is recording. The note body offered that button only while a recording was running, so a long note - the whole screen on a phone - could only be scrolled back down by hand. It is now the same button, in the same place, that Foxl Agent's chat uses, on the note body, the transcript panel and the AI panel, on desktop, iOS and Android.
- On a phone that button was partly behind the Notes action bar, and tapping it opened Ask AI. The note body claimed the strip of the screen the bar sits on, so anything anchored to the bottom of the note landed under it - measured at 32 of the button's 44 points, with the centre of the tap going to the wrong control. The body now ends where the bar begins.
Changes
- Foxl Code Settings no longer has an "Instructions & MCP" screen. Both controls on it promised more than they delivered: the custom-instructions box said it was appended to the agent's system prompt when the text was actually prepended to your own prompt, and the MCP connectors box accepted a JSON blob that was written into the task runtime with no check that any of those servers were reachable, no place to put a credential, and no effect at all on the assistants that do not read that file. Rather than leave a half-built screen in Settings, it is gone. Autonomy, Coding agent, Defaults & budget and Notifications are unchanged (Container is new this release, see above), and text you had saved is still in the database rather than deleted, so nothing is lost if this comes back done properly.
- Workspace storage and Channels bills now land after your credits arrive, not before. Both were charged on the day of the month you signed up for Foxl, while the credits that pay for them arrive on your subscription's renewal day. Those are different days for every paying customer, so the bill could land while your balance was at its lowest point of the month, and for storage that meant new uploads were paused for an account that was fully paid up. Both meters now use the same renewal date the rest of the billing page does, including its time of day.
- A workspace bill that could not be paid is no longer forgotten. If a storage or Channels charge failed because the balance was short, it was retried only until the end of that calendar month and then never again. The unpaid period is now recorded and retried, oldest first, once credits are available, and it shows up in the workspace's billing history as unpaid instead of being invisible.
- An upgrade whose confirmation from our payment provider went missing now completes on its own. Moving from Pro to Ultra takes effect when the prorated invoice is paid, and that arrived as a single notification. If it was lost, the account stayed on the old plan with the old monthly credits indefinitely, and the five-minute reconciliation could not correct it. It now asks the provider for the invoice directly and applies the plan the moment it sees it was paid.
- Foxl Code no longer starts a task for an account that has run out of credits. On a paid plan there was no credit check at task start, so a task could open, spend real compute, and then fail every model call because credits were exhausted. It now declines with a clear message before anything is spent. A task already running is unaffected and can still overshoot, as before.
- A free workspace no longer sees an estimated storage or Channels bill. Free plans are never charged for either, but the forecast priced them anyway.
- After a failed payment, credit usage is measured from the right date again. When a subscription lapsed, the old renewal date was kept as the usage-window anchor while credits went back to refilling on your signup date, which inflated the "used this period" figure for the rest of the period.
- Shell commands ask for your approval again. Foxl ships with rules that make the agent stop and ask before it runs a shell command, opens a terminal, or manages a background process. A separate rule was also being written on first launch that auto-approved every tool, and it outranked the three that ask - so on every install those prompts never appeared, and anything the agent read could put a command on your machine without your say-so: a web page it fetched, a file in the workspace, a Slack mention, the text of an issue. The blanket rule is gone, and it is removed from existing installs on the next launch. If you turned "auto approve all" on yourself in Settings, that choice is untouched. Two routes to a shell were still unprompted underneath that rule and are closed with it: typing into a terminal the agent did not open (it can list the ones you opened yourself), and writing to or killing a background command. Reading a terminal, polling a running command and fetching its log still do not interrupt you, because those are how the agent watches work it already started.
- Your saved provider API keys can no longer be sent to somebody else's server. The endpoint that tests a key accepted the address to test against and the key to use as separate, independent inputs, so a request naming an outside address and no key was answered with your stored key in plain text. A custom address now has to come with its own key; the stored one is only ever sent to the address you configured.
- Saved provider API keys are harder to read out of a stolen database. They were encrypted with a key computed from the machine name and the install path, both of which anyone holding the database file can guess, so the encryption added nothing against a copied backup. The key is now random, kept in a separate protected file, and existing keys are re-encrypted the first time they are read.
- Credit warning emails now reach everyone, including anyone who has bought extra credits. The 80% and 100% notices measured your remaining balance against your plan's monthly allowance, so any account holding credits beyond that allowance - every account with signup credits, and every top-up ever purchased - produced a negative figure and was silently skipped. A free account with signup credits scored -2000%, and on Pro with one 1,000-credit top-up the "80%" notice could only arrive once about 93% was really gone. The notices now use the same period figure your account page shows, so the two can no longer disagree.
- A relay hiccup no longer signs you out. Any failed token refresh was treated as a dead session, so a brief server error, a dropped connection, or two of the app's own screens refreshing at the same moment could end with you back at the sign-in screen on a session that was perfectly valid. Only an actual rejection clears it now; anything else is retried.
- A relay hiccup no longer clears Foxl Notes' local data. The same failed refresh ran a full sign-out inside Notes, which discards offline transcripts, notification history, your avatar, standing tool approvals and any provider key you had entered - at exactly the moment offline data matters most. Notes no longer signs anybody out.
- Signing in on two windows at once can no longer log you out of every device. Four separate places refreshed the sign-in token against the same stored copy with no coordination, so a slow reply could put an already-used token back on disk. The relay reads a reused token as a stolen one and ends every session on every device. There is one refresh path now, and a reply that arrives late is discarded instead of overwriting a newer one.
- The chat composer's send button no longer slides off the screen at large text sizes. The row could not give: the model name held its full width, so everything after it was pushed past the edge and there was no way to send a message. The name shortens now and the send button stays put. Affects the in-app UI font-size setting as well as the new iOS text-size support.
Fixes
- "Keep running when closed" now picks up an update without waiting for you to open the app. The background service had no updater of its own, so after an update installed itself the machine your phone talks to kept running the previous build until the app was next launched - measured at 21 minutes of a 0.6.0 server answering an iPhone, and in practice as long as you leave the app closed, which is the whole point of the setting. The service now notices that the build on disk is no longer the one it is running and restarts into it within about two minutes. On macOS and Linux it replaces itself completely; on Windows it replaces the part that serves your phone and the rest is updated the next time you open the app.
- Scrolling back through a live transcript no longer drags you to the bottom. Notes followed the newest line unconditionally, and during a recording a line updates every few hundred milliseconds - so scrolling up to re-read something was undone before you got there. The transcript, the AI panel and the note body now follow the newest text only while you are already at the bottom; scroll away and they stay where you left them and offer a jump-to-latest button, exactly like chat and agent. Opening another note starts at its newest line again.
Features
- "Restart service" next to the background-service status. When the service is running but its server is wedged, the only ways out were turning the whole feature off and on - which restarts Foxl - or the server-crash panel. There is now a restart link on the status line itself, and it reports the outcome rather than just firing off the request.
- Starting a huddle now tells the other person. A huddle told nobody it had begun: you pressed the headphones, the header said "1 person", and whoever you wanted to talk to found out if and when they next opened the channel. In a DM there was no way to fix it by hand either, because a DM has no invite picker. Starting one now rings the other side of a DM - on their phone too - and posts a "started a huddle" line in the channel, so the call leaves a trace in the conversation and moves it up the sidebar. A huddle in a channel does not ring everybody in it; the line plus the pulsing headphones is the signal there, and the invite picker still works for calling someone in.
- "Talk to us" on both pricing pages is a form now, and it records the enquiry. It used to be a
mailto:, which left no record anywhere: the enquiry landed in one inbox, there was no team size to price against, and nothing counted how many conversations a deal takes. The form asks for a work email, company, team size and what they would have Foxl do, and posts to a new relay endpoint. If the post fails, the email address is shown as a fallback so an enquiry is never lost. - A daily growth digest. The relay's nightly pass now records one dated snapshot of the funnel - signups, activation, paid accounts by tier, MRR, the pipeline by stage, and how many interviews one close costs - and emails it when a recipient is configured. The subject line carries the blocker rather than a greeting: while checkout is unconfigured it reads
checkout DISABLED, because MRR is then pinned at zero by configuration rather than by demand.
Fixes
- An update that does not install now says so, instead of leaving you on the old version. Pressing "Install now" hands the new build to the operating system's installer, which runs as a separate program and replaces Foxl after Foxl has quit. If anything is still holding the app when it gets there - a leftover helper, a window that reopened mid-quit - it cancels the swap, and until now nothing anywhere reported that: the app restarted on the old version, the bell said the update was ready to install, and only trying again a second time actually worked. Foxl now records which version it handed over and checks on the next launch. If that version is not the one running, the bell and Settings say the install did not complete and offer to try again, and the log names the installer's own log file.
- And it is much less likely to happen in the first place. Foxl now makes sure it has really exited once the installer is waiting on it, and the main window refuses to be recreated while the app is quitting - so a click on the Dock icon in that last moment can no longer bring the app back and cancel its own update.
- No more "WebSocket was closed before the connection was established" error box. Restarting the app while its local broadcast connection was still being established raised an uncaught-exception dialog, most visibly right after an install or when turning the background service on. The connection is now torn down quietly, which is what a teardown should always have been.
- A restart you asked for no longer reports itself as a crash. Turning the background service on, restarting, and installing an update all disconnect the local agent for a moment, and the "Agent server stopped" panel could not tell that from a real failure - so it appeared mid-restart and began restarting the server underneath, which on an update is one of the things that can cancel the install. The app now says when a shutdown is deliberate, and the panel stays out of the way.
- The emoji picker closes when you pick an emoji. It stayed open, so the reaction you had just added was hidden behind the panel you had to dismiss by hand.
- A "started a huddle" line reads as an event, not as a message somebody typed. Server-written messages rendered with an avatar, a name, reactions and an edit/delete menu that acted on a row nobody authored.
- The huddle button in the channel header no longer wears a count while you are in the call. A small number pinned to a red button reads as something wanting attention, directly above a bar that already lists everyone in the room.
- Channels opens on the conversation you left, instead of a spinner. The channel list and the open channel's transcript were re-fetched from scratch on every visit - and every navigation in the app is a fresh mount - so leaving Channels and coming back, or switching between two rooms, cost a round trip to the server each way and flashed an empty conversation in between. Both are now painted immediately from what you last saw and refreshed behind it.
- The channel list no longer flashes "Loading" while it is up to date. The sidebar refreshes itself every 20 seconds and after every mute, star or mark-read, and each of those showed a loading row over a list that had not changed. Only a genuinely first load says it is loading now.
- Fewer requests per visit. Opening Channels asked the server to seed
#generalon every mount rather than once per workspace, re-read the open channel's member list every 20 seconds, and marked a channel read twice on a single click. - Your account and credits screens no longer get slower the more you use Foxl. Both of them, and the companion's gem counter, worked out the totals by adding up every single request on your account from scratch each time they were opened - so the busier the month, the longer they took, and there was no upper limit to it. They now read a running daily total instead. The numbers are identical, including the token count the companion earns from; what changes is that opening the screen costs the same on your thousandth request of the month as on your first.
- A desktop whose sign-in had expired stopped retrying every minute. It would ask the server to renew credentials the server had already refused, once every 45 seconds, indefinitely - it was doing nothing useful and it was most of the traffic Foxl's servers were handling. It now waits between attempts when the answer was a refusal rather than a hiccup, and signing in again still reconnects immediately, as before. A connection lost to a network problem or a server outage retries just as quickly as it always did.
- Notifications look like a notification list again, on the phone and on the full page. The bell sheet and the Notifications page each drew their own rows over the same inbox, and both had the same three problems: every row carried a dot (a near-black one when unread, a grey one when already read, so the marker meant nothing), the archive button was pinned over the row and landed on top of the body text of any two-line notification, and on the page that same button became a full "Archive" label inside the row, squeezing the text into a narrow column. Rows now follow the standard iOS list: a blue dot only when unread and nothing when read, a coloured tile per notification type so an approval request, a channel message and a schedule result tell themselves apart, the timestamp and the file button side by side in the row rather than over it, and separators that start after the tile. Approve / Deny are quiet tinted buttons instead of solid black and red pills, day headings are sentence case, and the page hero no longer states the same count three times.
- The Notifications page gained the things only the bell sheet had: a swipe to delete or to mark read again, and the buttons on a notification that is asking you something - an approval request could previously only be answered by opening the bell over the page that was already showing it.
Internal
- Plan list prices ($20 Pro, $200 Ultra, enterprise quoted) exist as a table in the code for the first time; they previously lived only in a doc comment and in the Lemon Squeezy variants, so revenue could not be computed at all. Enterprise is deliberately
nullrather than a number, and the price is NOT derived from the credit allowance (Ultra sells credits at half the Pro rate, so arithmetic would report double its real MRR).
Features
- Work opens on a "For you" queue that spans every repository. Green pull requests waiting on a press, failing builds, questions Foxl is blocked on, unclaimed issues and Foxl's own proposals now arrive in one ranked list, closest to done first. Before, the Work tab was one repository at a time and nothing told you which of them was waiting - so with three repositories connected you visited three tabs to find out. Every row carries the repository it belongs to, one line of why it is there, and its buttons: Merge, Fix it, Solve, Decline, Diff, plus Dismiss and Snooze.
- Decline an issue without opening GitHub. Any issue can be closed as "not planned" from the Work tab with a reason, which is posted as a comment so the trail exists outside Foxl. This was the most common triage decision and the only one that still needed a browser trip - and it is what frees Foxl to look for work again, since it stops surveying while its own filed issues are unread.
- Ask Foxl to review a pull request, from the product. An "Ask Foxl to review" button runs the independent reviewer on any PR in a connected repository, and "Request changes" records your own verdict and sends the agent back to address it. Both existed server-side and had no button anywhere, so the only way to trigger a review was pressing Approve on github.com.
- Foxl learns from a No. When you dismiss or decline something with a reason, the next time Foxl goes looking for work it is told what you turned down and why, and does not propose it again.
- Autonomy is one choice instead of six switches. Code Settings now leads with three options - only when I ask / work on its own, ask me to merge / merge its own green work - and the middle one is the default. The six individual switches behind them are all still there under "Advanced", and picking a preset changes nothing else on the page.
- Foxl Code's settings are one row per topic. The Settings sidebar lists Autonomy, Coding agent, Defaults & budget, Instructions & MCP and Notifications under a Code heading, and Recording, AI features, Live translation and Auto-save & export under Notes. Each product used to be a single row holding its entire settings page, so a switch was findable only by scrolling.
- "When Foxl works on its own" moved to Schedules. How often it checks on running work, its quiet hours and how often it looks for new work now sit beside your repository schedules, because that page already answered "when". The daily spend cap moved next to the per-task budget, for the same reason.
- Page-view measurement on the Foxl websites, and only if you accept it. foxl.ai, docs.foxl.ai, app.foxl.ai and code.foxl.ai can now count page views with Google Analytics. Nothing is loaded and no analytics cookie is set until you say yes: decline and the tag is never fetched at all, rather than fetched and asked to behave. Your answer is remembered across all four sites, so you are asked once, and you can change it at any time from the new Cookie settings link in the footer of foxl.ai - withdrawing stops collection immediately instead of at the end of the visit. The apps are deliberately not included. The Foxl desktop app and the Foxl apps for iOS and Android contain no analytics SDK and send nothing, which is why they never show the consent notice - there is nothing there to consent to. The web bundle they embed is the same one app.foxl.ai serves, so the tag is gated to an explicit list of two web hostnames rather than switched off per platform: a new place this bundle ever gets installed starts with analytics OFF and has to be added by name. The privacy policy has been updated in the same change: a new section 3 covers cookies and this choice, Google is named in the list of companies that process data, and the places that previously said "no analytics" now say what is actually true - the apps do not, the websites do, with consent.
Fixes
- A pull request cannot be merged on an approval unless its build passed. When Foxl reviews a PR you approved and its own verdict is set to merge, it now refuses unless CI reported green on the exact commit being merged. It had no build check at all on that path, so a red pull request approved on github.com could merge - and because branch protection is unavailable on this plan, that check was the only one there would have been.
- Foxl stops waiting on work that is already dead. A task stuck part-way through used to count as live for a fortnight, and while anything counted as live Foxl would not go looking for new work. A run that has shown no sign of life for longer than one can possibly last no longer holds the queue open. Nothing is cancelled any earlier than before; only the "is anything actually moving" question changed.
- "Find work when the queue is empty" is on for accounts that never chose otherwise. The switch was flipped on by default some releases ago, but any account that had opened Settings even once - for any unrelated reason - stayed on the old off value forever. If you deliberately turned it off, it stays off.
- The Settings page explained the wrong field. The note describing how many issues Foxl may leave open was printed under "What to focus on", so the limit had no explanation and the box beside it described something else.
- Connect GitHub works. On code.foxl.ai the button flashed and put you back on Tasks - no GitHub approval screen, nothing connected, no error - and it did that for every user in every browser. The server was fine: Cloudflare was answering the request from the static bundle without ever running the code that forwards it to GitHub, which it does for any request a browser makes by NAVIGATING to a page. Every investigation with
curllooked healthy becausecurldoes not send the header that triggers it. All three GitHub-facing addresses were dead the same way, so installing the app by hand could not work around it either. - Cmd+Q works again after you turn on "Keep running when closed". Switching that setting restarts the app, and the restart is arranged by a small helper process that waits for Foxl to exit and then starts it again. If the quit it was waiting for did not go through, that helper stayed behind attached to a running app, so the next time you quit Foxl for real, hours later, it was immediately relaunched. Quitting looked broken and nothing said why. The restart is now arranged at the last moment, once the app is genuinely on its way out, so a quit that does not complete leaves nothing behind. The reason a quit could fail to complete is fixed too: the floating Notes meeting window is prepared invisibly about eight seconds after launch so its first open is instant, and if a quit was already under way at that moment, preparing it cancelled the quit. No window is created while the app is closing now, including the meeting window, the huddle window and the desktop companion.
- The background service comes back on its own if something kills it. With "Keep running when closed" on, an OS service holds the local server and the phone connection. Anything that stopped that service without turning the setting off left it stopped, and the machine stayed unreachable from your phone until the next time you opened the app, with the switch still reading as on. The service now tells the system it wants to be restarted in that case, while a stop you actually asked for (turning the setting off, or an app update replacing the service) still stops cleanly. On macOS and Linux that means it is back within about ten seconds; on Windows the service is started by signing in, so it still waits for your next sign-in.
- Turning "Keep running when closed" off no longer leaves the machine unreachable. Both directions of that switch restart the app, because the local server and the phone connection change hands between two processes. When that restart silently did not happen, the app kept serving locally with no phone connection at all until some later launch. The restart is now forced through if it stalls.
- Every screen on your phone has a name again, at the size it was designed at. The page title was meant to be 28px; it had been rendering at 22px on every screen, in both products, because a single stylesheet rule aimed at the whole app was quietly overruling each screen's own setting. The same rule was flattening the other direction too: a 14px channel label and a 28px page title came out identical. With nothing on a screen bigger than anything else, no screen had an obvious starting point to look at, which is most of why the phone felt busy. Titles now render at the size they ask for.
- Foxl Code's screens use the same title as the rest of the app. Tasks, Work, Inbox, Repos and Runtimes had a second, smaller title of their own, so moving between the two products on one phone meant two different-looking headers in the same app. There is one now.
- The top of a phone screen no longer explains the feature to you. Work opened with three lines describing what issues and pull requests are; Inbox with two comparing itself to Work. Both are useful the first time and never again, so they are kept for wider screens and the phone goes straight to the content. An empty Notifications screen also said there was nothing to read three separate times; it says it once.
- Schedules shows your schedules sooner. A three-cell Total / Enabled / Total runs strip sat above the list restating the line directly above it, with two of its three cells usually reading 0. It is a desktop thing now, and the first schedule starts about 80px higher up the phone.
- Repos offers to connect GitHub once instead of twice. With no repos connected there were two identical buttons on the same screen; the one beside the explanation stays.
- The red number on the iPhone icon is the real count now, not always 1. Every notification Foxl sends carried a hardcoded badge of 1, so three replies arriving while the app was closed still showed 1, and the number only became true once you opened the app - which is the moment it stops being useful. The count is now kept per person and advances with each notification, and the app publishes what its own bell says whenever that changes, so the bell, the icon and the notification agree instead of each counting something different. Reading everything still clears it. Product updates and the occasional companion notification no longer touch the number at all: they add nothing to your inbox, and a badge on them used to reset a genuine count of five things down to 1.
- A huddle invite is marked time-sensitive, so a ring can reach you in Do Not Disturb. Every notification went out at the same normal priority, which Focus modes are free to withhold - and a huddle invite expires after two minutes, so the one notification that behaves like a phone call was the one guaranteed to be silently held back when your phone was face down. Everything else stays normal on purpose; a finished task or a colleague's message reads fine later.
- A follow-up notification replaces the one before it instead of stacking. A coding task that became ready to merge and then merged left two banners on the lock screen saying different things, and only one of them was still true; an expired huddle invite sat beside the live one. Updates about the same conversation, task, question, note or call now collapse onto the previous banner. Separate things stay separate - two colleagues' messages, two different issues, and a transcript and its summary are all still their own notification.
Features
- Click the companion out on your desktop and Foxl comes back to the front. The reverse of Cmd+W: that shortcut hides the window and leaves the animal out on the desk, which made it the only piece of Foxl still on screen and yet the one thing that could not bring the window back - the Dock or the tray were the only ways in. A left click now raises the window (re-creating it if it had been closed, and restoring it if it was minimized) alongside the reaction it already played. Dragging the animal still changes nothing about your window order, and right-click is still its menu. The same on macOS, Windows and Linux. That takes slightly different work per platform: the companion is deliberately a window that never takes focus, and Windows and Linux both refuse to let a program that is not already in front hand itself the focus, so the window is brought forward by z-order there rather than by asking for focus and hoping.
Fixes
- The desktop companion's menu no longer leaves the animal stuck to your pointer. Right-clicking it and choosing "Stay here" or "Let it wander" dragged the pet onto the menu row and then kept it glued to the cursor, with no button held, for the rest of the session - it also stopped wandering, because a pet being carried does not walk. Two causes, both fixed: a right-click was arming the drag as well as opening the menu, and the release that would have ended that drag is swallowed by the menu itself, so the gesture was never closed out.
- The desktop companion walks both ways again. It was drawn facing right no matter which direction it was heading, so it moonwalked through half of every trip. The in-app pet has always turned around correctly; out on the desk nothing was applying the mirror.
- A desktop companion's hover reaction now lasts exactly as long as you are hovering it, the same as in the app. Because the window out there IS the animal, the pet walks into a cursor that is parked over some other app - so the greeting was firing with nobody hovering anything, and then playing for a further two seconds after the pointer had gone. It also started while the cursor was still up to 22px clear of the art, since the window is a little larger than the drawing inside it. A deliberate click still gets its full reaction whether you keep the pointer there or not.
- Cmd+B did two things at once, and only one of them was asked for. The Channels message bar's formatting shortcuts (Cmd+B bold, Cmd+I italic, Cmd+Shift+X strikethrough, Cmd+E code, Cmd+K link) were bound on every chat composer in the app, and Cmd+B is also the shortcut that collapses the sidebar - so one press in the Agent chat wrapped the draft in
**AND folded the sidebar away, on a screen that has no formatting bar to explain either half. The formatting shortcuts now belong to the Channels message bar only, and they are exclusive while it has focus: Cmd+B there applies bold and leaves the sidebar where it was. Everywhere else (Agent chat, Foxl Code chat, and every other screen) Cmd+B is the sidebar again and nothing writes a mark. The Channels shortcuts work whether or not the "Aa" toolbar is showing, which is what its own tooltips advertise. - "Keep running when closed" now appears as Foxl, with the Foxl icon, in System Settings > Login Items. It was listed under the developer's name with a blank placeholder icon, which said nothing about which app had asked to run in the background. macOS names a background item after the app it belongs to, and the background service was not telling it which app that was. Machines that already have the setting on are corrected on the next launch; there is nothing to switch off and on again.
- Turning "Keep running when closed" on no longer looks like Foxl crashing. Handing the server over to the background service tripped the app's own crash-recovery, so the moment the switch went on the window dropped to the "server stopped" screen - and pressing Restart there made it worse, because it restarted a background service that was already healthy and left the machine unreachable for around twenty seconds each time. Foxl now waits for the background service to take over, says it is restarting, and restarts itself so it comes back attached to it. Turning the setting off restarts the app too, which is what reconnects your phone and the web app to it - before, that only happened the next time you opened Foxl.
- The Restart button reported failure while the background service was running perfectly. With "Keep running when closed" on, Restart handed the job to the service correctly and then spent thirty seconds waiting for a signal that, in that mode, nothing sends - so it always ended on "the server did not come back".
brew install --cask foxl-ai/tap/foxlinstalls the current version again. The Homebrew cask had been stuck on 0.5.14 since August 6: three releases went out without it, so anyone installing or reinstalling through Homebrew got a build from a week earlier. Publishing the cask is part of the release itself now, and the release checks the cask afterwards rather than assuming it moved.
Features
- Work opens on a "For you" queue that spans every repository. Green pull requests waiting on a press, failing builds, questions Foxl is blocked on, unclaimed issues and Foxl's own proposals now arrive in one ranked list, closest to done first. Before, the Work tab was one repository at a time and nothing told you which of them was waiting - so with three repositories connected you visited three tabs to find out. Every row carries the repository it belongs to, one line of why it is there, and its buttons: Merge, Fix it, Solve, Decline, Diff, plus Dismiss and Snooze.
- Decline an issue without opening GitHub. Any issue can be closed as "not planned" from the Work tab with a reason, which is posted as a comment so the trail exists outside Foxl. This was the most common triage decision and the only one that still needed a browser trip - and it is what frees Foxl to look for work again, since it stops surveying while its own filed issues are unread.
- Ask Foxl to review a pull request, from the product. A "Ask Foxl to review" button runs the independent reviewer on any PR in a connected repository, and "Request changes" records your own verdict and sends the agent back to address it. Both existed server-side and had no button anywhere, so the only way to trigger a review was pressing Approve on github.com.
- Foxl learns from a No. When you dismiss or decline something with a reason, the next time Foxl goes looking for work it is told what you turned down and why, and does not propose it again.
- Autonomy is one choice instead of six switches. Code Settings now leads with three options - only when I ask / work on its own, ask me to merge / merge its own green work - and the middle one is the default. The six individual switches behind them are all still there under "Advanced", and picking a preset changes nothing else on the page.
- Foxl Code's settings are one row per topic. The Settings sidebar lists Autonomy, Coding agent, Defaults & budget, Instructions & MCP and Notifications under a Code heading, and Recording, AI features, Live translation and Auto-save & export under Notes. Each product used to be a single row holding its entire settings page, so a switch was findable only by scrolling.
- "When Foxl works on its own" moved to Schedules. How often it checks on running work, its quiet hours and how often it looks for new work now sit beside your repository schedules, because that page already answered "when". The daily spend cap moved next to the per-task budget, for the same reason.
- Open an issue, get an agent. Filing a GitHub issue in a connected repository now starts a coding agent on it - no button, no chat turn. Before, an opened issue reached nothing at all: the only way from an issue to an agent was the Solve button on the Work tab, so an issue Foxl filed for itself and did not immediately pick up sat there forever. It only acts on issues opened by someone whose GitHub account is linked to Foxl and who is still a member of that installation, it will not start a second agent on an issue that already has one, and it respects the same in-flight task limit as everything else.
- Foxl Code can be pointed at one repository. Settings -> "Find work when the queue is empty" now takes a repository name. Left empty it keeps following you around, looking at whatever you worked on last; name one and it stays on that project - the one you would never get round to asking about. It still only looks at repositories you have actually run a task in.
- It remembers what went wrong. When a task fails, Foxl now writes down one line about why, and hands that to the next task in the same repository. A retry used to start knowing nothing about the attempt it was replacing.
- Settings > Appearance can put a soft vertical gradient on the app's chrome. Two presets - Dawn (cream fading to pale blue) and Mist (a single-hue blue) - wash the sidebar, the outer canvas and the window titlebar from top to bottom, while the content area is never tinted. Each option is shown as a real swatch rather than a name, so the choice is visible before it is made, and the presets compose with Light, Dark and System. Default is unchanged: an existing install keeps the flat surface it has today, and Default sits first in the picker. Text stays exactly as legible as it is on the flat surface. A gradient can only be darker than the surface it replaces, so the sidebar's labels are re-derived to hold their current contrast over the deepest point of each ramp - measured rather than eyeballed, in both themes.
- "Keep running when closed" (Settings > Desktop): your computer stays reachable from your phone and the web after you quit Foxl. Until now the local server and the relay connection both belonged to the app window, so closing it took the machine offline - which is awkward for something described as a 24/7 agent. Turn this on and Foxl installs a small background service (a launchd agent on macOS, a systemd user unit on Linux, a Task Scheduler task on Windows) that owns the server and the connection instead. The app then attaches to it when you open it, and quitting changes nothing.
Off by default, and turning it off puts everything back the way it was. When it is on, the app no longer starts a second server of its own - the two would fight over the same port - and the Restart button asks the background service to restart rather than killing it.
Notes: the background service runs headless, so desktop notifications and app auto-update still need the app open (a question from Foxl Code reaches you through your phone and the in-app panel as usual). On Linux the installer also asks the system to keep your user services alive after logout; if that prompt is declined, Foxl says so and the service runs until you log out. Logs are in
~/.foxl/logs/daemon-*.logand are pruned after 14 days.
Removed
- Foxl Code's Knowledge page is gone, along with its three tabs (the repository memory list, the security Activity log, and the prompt Inspector). The sidebar row, the phone tab-bar entry, the command-palette entry and the
/knowledgeURL went with it; that URL now lands on Tasks like any other unknown path, and anyone who had put Knowledge in their customised tab bar keeps the rest of their layout. The agent's repository memory itself is UNCHANGED - it is still written and read by the coding agent and still injected into every task prompt for that repo. What was removed is the read-only viewer over it, not the memory.
Fixes
- Notes: a sentence spoken across a transcription handover no longer sticks to the bottom of the live panel - and no longer goes missing from the saved note. Transcription reconnects every 4.5 minutes, and the reconnect used to discard the closing line each time: the service sends a sentence's finished version last, after being told the audio is done, and Foxl hung up before it arrived. The unfinished line then stayed unfinished for the rest of the meeting. Because the live panel shows in-progress speech below everything already transcribed, it parked itself under the newest text and covered it, with no speaker name and no error - a 15-minute call produced three of them. The same line was also absent from the note, its summary, its export and its shared text, all of which keep finished speech only, and its translation stayed stitched together from half-sentences. Foxl now waits for that closing line before hanging up, and if it never comes (the network dropped, the laptop slept) it keeps the words that were already transcribed instead of discarding them. The same fix applies at Stop, so the last thing said in a meeting is saved rather than dropped.
- The bell showed no number over an inbox that had unread items in it. A notification whose destination was the page you were already on was marked read the moment it arrived - within the same frame it was filed, before it had been on screen at all. Chat replies, channel messages and Foxl Code task updates all land on pages people sit on, so the count they were supposed to raise was the count most likely to be erased. The row stayed in the list, which is why the two surfaces disagreed: items visible in the sheet, nothing on the bell. Arriving on a page now reads the notifications that were already waiting there and leaves anything that arrives after you got there unread. A streaming reply no longer counts as arriving on a page either - every message appended to the open conversation used to re-run that check.
- The red number on the app icon did not match the one on the bell. They were computed differently: the bell counted the notification inbox, while the icon added unread conversations on top, so a phone could show three on the home screen and one on the bell for the same list - and tapping through every notification could not clear it. The icon badge is now exactly the bell's count, so everything it counts is reachable and it can always reach zero. The macOS dock badge deliberately keeps counting unread conversations alongside notifications, because on the desktop a reply that lands in a conversation you are not looking at files no notification of its own; it matches the sidebar's Chat row, which counts the same thing.
- Reading your notifications in the app left the banners on your lock screen. The app already respected the phone's notification centre in one direction - swipe a banner away and the in-app row went read - but never the other, so clearing the bell changed nothing outside the app and the same items kept sitting in Notification Center. Rows you have read or archived now have their banners removed, and so do rows you swipe away or clear, which was the case where the app's list was empty and the phone's was not. Unread items are left alone in both places.
- The pet showed a companion you did not own, and "Follow along" could land on a character you had never drawn. The gallery locked every unmet card correctly, but the code that actually draws the animal never asked what the account owned - so turning "Show pet" on with an empty collection produced a companion anyway (the fallback was a real 4-star doll from the summon cast), and "Follow along" could pick up a common that had merely been SIGHTED elsewhere in the app rather than drawn. Gifting away your last copy of a character left it walking around too, since the copy is spent the moment the gift is sent. All three now go through the same ownership check the gallery uses: with nothing owned the pet stays away and the Show pet row says to draw a companion first. Nothing changes for anyone who owns what their pet is set to, and a signed-out or offline app keeps drawing the companion it was already showing.
- The companion counter in Settings > Pet could never reach its own total. It counted the nine dolls but printed that against all fifteen cards, so an account that owned every companion in the pane read "9/15" and no amount of playing could complete it. The two numbers now come from one set: the nine dolls, which is the collection the draw is for - the six commons are the pool a draw comes out of rather than the prize, so a player's first two draws no longer look like progress they did not make.
- The chat's first screen could be scrolled, and there was nothing below it. Opening a new chat on the desktop left the transcript scrollable by exactly 20px at every window size - and scrolling moved nothing, because the greeting and the composer are pinned to the middle of the canvas. The scroller was reserving room for a floating composer to overlap the last message, on a screen that has no messages: while empty, the composer centres itself by filling the whole canvas, so the space reserved for it was the height of the window.
- Real iOS glass came back to the round buttons, and had been gone since v0.5.12. The header chips on iPhone are supposed to be actual iOS 26 Liquid Glass (a UIKit material, not a CSS blur), and they had quietly reverted to the CSS imitation for five releases. Nothing was switched off for performance: the native plugin lives outside the app's own source tree, and the file naming where to find it was written with a path three folders too deep, so the build looked for it somewhere that does not exist and linked nothing. Because the app is meant to fall back to CSS glass on older iPhones, a plugin that could not be found and one that is not supported look identical from the outside - so the build passed, the upload passed, and TestFlight marked it valid with the glass missing. The Agent screen's Back and Stop buttons now take the native material too. They had been left on CSS glass while the shell header beside them used the native one, so opening an agent swapped the treatment mid-screen. The message bar, the Channels surfaces and the bottom tab bar keep CSS glass, which is a limit of the platform rather than a choice about effort: the native material refracts every pixel inside its own rectangle, so it can only be given to a control whose entire foreground can be handed to UIKit - in practice a single icon, drawn as an SF Symbol on top of the material. A message bar holds a live text field, a model picker and two buttons; a tab bar holds four icons, four labels and a badge; the Channels list holds rows of text. Those came out smeared when it was tried, because a native view sits outside the web page's paint order and no stacking trick lifts web content above it. CSS glass is the correct tool wherever content sits ON the surface, and it is what those three keep.
- The companion can come out of the app window and live on your desktop. Settings > Pet has a "Show outside the app window" switch, and the pet's own right-click menu offers to let it out - after which it walks around your desk over your other apps, follows your cursor when asked, and can be picked up and carried anywhere, including onto another monitor in one continuous drag. Its menu offers the way back into Foxl. Off by default, and desktop-app only: a browser tab cannot paint outside itself, so the row explains itself rather than disappearing there. This shipped as a working feature on the third attempt, and the difference is the shape rather than the polish. The two earlier versions made the pet a transparent window covering a WHOLE DISPLAY that had to become click-through everywhere except the animal - so any bug that left it interactive stopped every click on that monitor until you quit the app, which is what happened. The window is now the size of the animal, about 76px, and is never click-through: there is no hit test, no gesture lock and no cross-monitor handoff, because a small window that always takes its own clicks needs none of them. The worst it can do is take a click in the 76px it occupies, which is the pet working.
- Open an issue, get an agent. Filing a GitHub issue in a connected repository now starts a coding agent on it - no button, no chat turn. Before, an opened issue reached nothing at all: the only way from an issue to an agent was the Solve button on the Work tab, so an issue Foxl filed for itself and did not immediately pick up sat there forever. It only acts on issues opened by someone whose GitHub account is linked to Foxl and who is still a member of that installation, it will not start a second agent on an issue that already has one, and it respects the same in-flight task limit as everything else.
- Foxl Code can be pointed at one repository. Settings -> "Find work when the queue is empty" now takes a repository name. Left empty it keeps following you around, looking at whatever you worked on last; name one and it stays on that project - the one you would never get round to asking about. It still only looks at repositories you have actually run a task in.
- It remembers what went wrong. When a task fails, Foxl now writes down one line about why, and hands that to the next task in the same repository. A retry used to start knowing nothing about the attempt it was replacing.
Fixes
- Connect GitHub works. On code.foxl.ai the button flashed and put you back on Tasks - no GitHub approval screen, nothing connected, no error - and it did that for every user in every browser. The server was fine: Cloudflare was answering the request from the static bundle without ever running the code that forwards it to GitHub, which it does for any request a browser makes by NAVIGATING to a page. Every investigation with
curllooked healthy becausecurldoes not send the header that triggers it. All three GitHub-facing addresses were dead the same way, so installing the app by hand could not work around it either.
Features
- The companions are plush dolls again, and there is a gallery to pick one from. The pixel art was drawn one animal at a time at 16x16, and at that size every character carried stitches, odd eyes, a bow, a bell, a patchwork seam and a tail within two pixels of each other - so instead of a shelf of toys it read as speckle, and no two of them were built the same way. Every companion is now generated from one shape language at 12x12: fewer, larger pixels, a heavier outline, and the marks their illustrations actually carry - the ragdoll's blue bow and gold bell, the grey cat's split panel and mismatched eyes, the bear's navy top hat. Because the shape is shared, no character can drift from the family. Settings > Pet shows the whole cast at once instead of hiding it behind a dropdown of names. Each card carries the character's ORIGINAL illustration at a size you can actually see, with the pixel sprite beside it at exactly the size it will be on screen - so the picker answers both what it is and what you will get, and the Size row has a live preview instead of three words.
- Companions are drawn, and most draws give you a common. The gallery has a Draw button that spends one gem, or five sparks when you have no gems - the same wallet and the same odds as the summon screen, which are published on both: SSR 0.3%, SR 0.7%, R 3%, and a common 96% of the time. Three commons joined the cast to fill that tier, so a draw always brings something home; it is usually the third sparrow. A character you have not met stays locked as a silhouette until it turns up, and after 60 commons in a row the next draw is guaranteed to be better. Gems come from real agent usage (one per 250k tokens, at most three a day) and sparks from tool calls. Neither can be bought, and there is still nothing for sale anywhere in this.
- The pet has a menu now. Right-click it on a desktop, or press and hold on a phone: pet it, call it over to where you pressed, tell it to stay put, draw a new friend, or put it away. It holds still while the menu is open, and a press that travels is still a drag rather than a menu.
- The companions are hand-drawn art at full resolution, and they act. Every character has a real four-to-six frame walk cycle and up to eight poses - standing, sitting, sleeping, waving, eating, crying, cheering, idling - drawn per animal rather than derived from one picture. Standing still, a companion drifts between its resting poses instead of holding a single frame; hover it and it waves, click it and it cheers. The art is no longer squeezed through a small grid, so the stitches, the bows and the fur read the way they were drawn.
- The companion lives where you put it. Drop it somewhere and that is its spot - it pooters around near there rather than setting off across the whole window, and it walks in short stints with long rests between them instead of pacing. Settings > Pet has a switch if you would rather it stayed perfectly still, and its menu has the same choice as a single row.
- Right-click it (or press and hold on a phone) wherever it is. The menu can send it after your cursor for a few seconds, park it where you pressed, draw a new friend, or put it away.
- Send a companion to someone. A character you own can be given away by email, with a note. The pet leaves your collection when you send it and arrives in theirs when they accept, so a gift moves an animal rather than creating one - and if the address has no Foxl account yet, the gift waits and is claimed the first time they sign in. An unclaimed gift comes back to you after a day, so a mistyped address cannot strand a character permanently, and you can take one back yourself before it is claimed.
- Gems and your collection live on your account now, not in the browser. They used to be stored locally, which meant they did not follow you between the desktop app, the web and your phone - and anything kept only in a browser can be edited by hand. A draw is now settled by the server, so the same wallet and the same collection show up everywhere you sign in, and a repeated tap on Draw cannot hand you the same character twice or spend the same gem twice. What is on this device already is moved up to your account the first time it syncs.
- The phone's bottom bar has a + now, and a lot more to put in it. Settings > Tab bar could change what was in a slot and drag slots around, but the only way to make the row LONGER was a 0-5 number picker that filled the new slot with a destination you never chose. Tap + instead and it asks: pick a destination and the row grows by one, up to five. The number picker is gone - the bar is as long as the tabs you put in it. The list of destinations you can dock is much longer. Tasks, Usage, Integrations and Mobile/Web are offerable for the first time - none of them had a way to be reached by tapping anything on a phone, since they left the sidebar for Settings - and Foxl Code's own Settings joins them. Any of them can sit in any product's row, so a Code queue or Notes can live in the Agent bar; tapping one switches product. An empty bar now asks which tab you want first rather than picking three for you.
- The agent's terminal is a screen of its own on the phone, in Foxl Code too. Opening Terminal on a task now pushes in a full-screen page from the right, the same way it does in Foxl Agent: just the agent's live output, and a keyboard built for it along the bottom. Swipe from the right edge to open it, swipe back from the left edge or tap the back chip to return to the conversation, which stays exactly where you left it. That keyboard carries the keys a phone does not have - Tab, esc, ^C, ^D, ^Z, del and the four arrows - so you can complete a path, answer a prompt, walk back through history, or interrupt a command that is running away. Before this there was no way to send any of them, which meant a running agent could not be stopped from a phone. Text goes through a proper command box, so Korean, Japanese and Chinese compose correctly instead of coming apart mid-word. The prompt and plan panels are not on this screen on purpose - they were taking the room the output needed, and both are still on the Conversation side. The desktop keeps its familiar layout, unchanged.
Changes
- Foxl Agent's workspace-files panel is now a rounded panel too. Opening it from the chat header slides in a white card that floats on the window's background, with a visible gap between it and the conversation, instead of a flat column meeting the transcript at a hard line. It is the same shape Foxl Code's review rail already uses, so the two products' right-hand panels now match. Resizing by dragging its edge, the file tree, the open-document tabs and the phone's full-screen version all behave exactly as before.
- Foxl Notes opens straight into the product. The five-step "Welcome to Foxl Notes" setup dialog is gone: the first visit lands on the notes themselves rather than on a language picker, a sign-in step, a microphone test and a page of tips. Nothing it configured is lost - the language lives in Settings > General, signing in is prompted where recording actually needs it, and the system-audio check is still a button in Notes settings. Settings > Notes no longer carries the "Show setup guide" row, since there is no guide to replay.
- A Foxl Code task that opened several pull requests now lets you see all of them. A plan that fans out across subtasks opens one PR per subtask, and resuming a task can land another - but the review panel could only ever show one, so the rest were unreachable from Foxl. When a task has more than one, its PR number in the review panel becomes a picker: choose any of them and the panel loads that PR's checks, files and diff, with the merge button acting on the one you are looking at. It opens on the same PR it always did, and a task with a single PR looks exactly as before - no picker where there is nothing to choose. Previously the second and later PRs were dropped from the task's record entirely, so they are now kept as well, along with whether each one is open, merged or closed.
- The right-hand review panel in Foxl Code is now a rounded panel of its own. Opening it slides in a white card that floats on the window's background, matching the way the sidebar already curves into the page, instead of a flat column meeting the content at a hard line. Dragging its edge to resize and collapsing it work as before.
- Removed the unused "bring your own Anthropic key" path on app.foxl.ai. The screen for entering that key was never reachable, so nobody could turn it on - but the relay half was live and would have skipped credit billing and the plan's model limits for anyone who sent the key by hand. Both halves are gone. This does not affect the desktop app's own provider keys in Settings, which are a different feature and work as before.
Fixes
- Sometimes there were several pets walking around at once. Each one was a real layer left behind in the page by an earlier mount, so they kept walking with nothing driving them from the app's side. The pet now clears any stray layer when it appears, and its animation loop retires itself instead of surviving a reload.
- A dialog opened from Settings on a phone was invisible to your finger. Any dialog that a control inside the Settings sheet opens - the tab bar's destination picker, the EULA - was drawn underneath the sheet that opened it. It was there, and every tap landed on the sheet instead, so the control simply did nothing. Dialogs and sheets now stack above the full-screen covers, like the menus and pickers already did.
- The tab-bar destination picker rises from the bottom on a phone now, on the same curve as every other sheet in the app, instead of zooming in from the middle like a desktop dialog.
- The Foxl Code terminal no longer sits in a slightly darker hole than the panel around it. In dark mode the terminal's own background had drifted a few shades away from the card it lives in, leaving a visible seam around the output.
- Foxl Code has its own sign-in screen again. code.foxl.ai used to open with a two-column door - the product's pitch on one side, the sign-in buttons on the other - and it went away when Foxl Code moved into the main app shell. Since then the site opened with the same generic screen as app.foxl.ai, which said nothing about tasks, repos or pull requests. It is back, with GitHub as the first option, since that is the account Foxl Code actually needs. Only the screen changed. Signing in with a password, the emailed link, and everything else about how sign-in works are the same as on app.foxl.ai, and app.foxl.ai, the desktop app and the phone apps all look exactly as they did.
- Arrange the phone's bottom bar yourself. Settings > Tab bar lets you set how many slots the row has - anywhere from one to five, or none at all - and choose what sits in each one. Drag the slots to reorder them, tap one to change where it goes, and turn the labels off if you would rather have more room for the icons. Destinations from the other products are allowed, so Notes or a Foxl Code queue can sit in the Agent bar; tapping one switches to that product. Each product keeps its own row, and the pane edits any of the three from one place. Nothing changes unless you change it: the bar ships with the same tabs it always had. Setting the row to zero slots removes it and gives that space back to the page rather than leaving a gap where it used to be.
- A real emoji picker in Channels. Reacting used to offer three emoji - a thumbs-up, a party popper and a pair of eyes - and there was no way to send a fourth, even though the server has always accepted any of them. There is now a picker with a search box, categories, and a "Frequently used" row that learns what you actually send. The three quick ones stay pinned where they were. It also opens on the message you clicked. The old strip anchored to the top of a group of messages from one person, so on any grouped message the buttons pointed at a different line than the one they would react to.
- Pick how much the channel list shows. Settings > Appearance now has a "Channel list" choice with a live example of each option: every channel with its last message and the time it arrived, or just the channel names, one tight line each - the classic Slack sidebar. Channels with unread messages go bold and full black in both. The same choice is on the workspace menu at the top of the list.
- Menus everywhere in Channels. Right-click a message for reactions, copy text, copy link, edit and delete. Right-click a channel for mark as read or unread, mute, star, add people, copy link, leave and archive. The workspace name and each section heading have their own menus too, with mark all as read, create channel, show only unread, sort by name, and collapse. The line under the workspace name used to read "All caught up" and do nothing. Marking everything read is now an action you can take rather than a state you are told about.
- Speak your message instead of typing it, in both chat and Foxl Code. There is a microphone in the message bar now. Tap it and talk; the words appear in the draft as sentences finish, and you can keep typing around them - dictate a clause into the middle of a sentence and it lands where the cursor is. What is still being heard shows under the bar in grey until it is committed, so nothing you say gets written twice. It is the same transcription Foxl Notes uses for meetings, so it handles ten languages and follows the language Foxl is set to. It runs on your Foxl credits and bills by the second, only while the mic is open - and it closes itself after a stretch of silence rather than leaving a hot mic running. The button is red while it is listening.
- A pet that walks around the app, with its own Settings pane. Settings > Pet turns on a small companion that wanders across whatever screen you are on. Pick it up and drop it anywhere and it stays put, then carries on from there - and it remembers where you left it. It walks with an animated step and turns to face the way it is going. Choose which companion appears, or leave it on Follow along, which keeps whoever you most recently brought out. Three sizes. Off by default, and it holds still when Reduce motion is on.
- Invite someone into a huddle, and answer with Join or Decline. A huddle used to be a room you had to already be looking at to know about: anyone in the channel could walk in, and nobody could be asked. Now anyone in a call can ring a specific channel member, and their phone rings too - the invite arrives as a card with Join and Decline, wherever they are. Decline is a real answer that travels back, so the person who invited you stops waiting instead of guessing whether you saw it. An unanswered invite expires after two minutes and says how long is left, because an invite that outlives its call would put you in an empty room. An invite that arrives while the app is closed is still there when you open it, as long as the call is still running.
- A huddle window of its own. On the desktop a call can be popped out into a small always-on-top window, so it stays visible and controllable while you are in a browser or an editor - which is when the in-app bar could never help, because Foxl was behind something else. On the web and on iPhone the same surface appears as a floating panel inside the app. Either way it shows what a single row could not: every person as a tile with their name, who is speaking, who is muted, and who could not connect. Closing the window does not hang up - Leave is the only thing that does. Notifications for huddle invites are their own switch in Settings, separate from channel messages, and muting a channel does not silence a call.
- Change workspace from inside Channels. The workspace name at the top of the conversation list is a button now, and it opens a picker of every workspace you are in. Previously that name was plain text that looked like a control and was not one: changing workspace meant opening the drawer, then Config, then the Workspace pane, then a row. With a conversation open there is a second one - the workspace name sits above the channel name. On a phone the list steps aside once you open a conversation, so before this there was nothing on screen telling you which workspace you were reading, and no way to leave it without going back first.
- React to, edit and delete a message on a phone. The reaction buttons only appeared on mouse hover, which does not exist on a touch screen - so on iPhone and iPad there was no way to react to a message at all. Editing and deleting had never been reachable anywhere. Press and hold a message, or use the button on its right, and all three are there.
- Schedules is now the same screen in Foxl Agent as it is in Foxl Code. The Agent side adopts Code's layout: a summary of how many schedules exist, how many are running and how many times they have run; one search box and one status filter that apply to whatever you are looking at; and three views behind a switch - a sortable table of your schedules, a calendar of the next seven days, and the run history. The run history used to sit permanently below the list with six filters of its own, so the schedules you came to manage were pushed off the screen by history you had not asked for. It is a view now, and it loads only when you open it. Creating and editing a schedule use one panel that slides in from the right instead of two separately-written dialogs, deleting asks in-app rather than through a browser prompt, and every row's Run now works independently - a slow schedule no longer freezes the button on every other row. The calendar reads its times from the same component that fires them, so what it shows is what will actually run, timezone and daylight-saving included.
- The phone app has a bottom tab bar. Chat, Channels, Workspace and For You on Agent; Tasks, Channels, Work and Repos in Foxl Code - all thumb-reachable at the bottom of the screen. Every destination used to sit behind one button in the top-left corner, which is the furthest point on a phone from the hand holding it. The side drawer is still there for everything else.
- Every screen says its own name. A phone screen now opens with a real title and, under it, one line of live state - whether your desktop is connected, how many unread messages are waiting, what is running. Before this the top of every screen was the same small grey word, so all of them looked alike.
- Channels looks like a messenger now. Each row shows who or what it is, the last message, when it arrived, and an unread count - with a face and an online dot for a direct message. Long-press a message to react to it, and there is a jump-to-latest button when you have scrolled back through history.
- The greeting is back on the phone home screen. "Good evening" over a few things to try, the way it looked before - and it now stays centred when a suggestion wraps onto a second line instead of drifting toward the top of the screen.
- The tab bar is a floating glass capsule. It sits inset from the edges and lifted off the bottom, so the page shows on both sides and underneath it - which is what makes the glass read as glass. The active tab is a pill that slides as you switch. Every screen scrolls beneath it, and the last row always clears it.
- One place for notifications on a phone. For You and the notification center were two screens showing the same things; the bell now opens For You, which is a tab.
- Copy and Regenerate stay on screen under the latest reply. They were revealed only by moving the mouse over the message, which meant they did not exist on a phone or tablet at all - a touch screen has no hover, so the buttons under the answer you had just received were invisible until you happened to tap the message itself. The reply at the bottom of the conversation now keeps its row visible everywhere. Earlier messages are unchanged, so scrolling back through a long conversation still reads as text rather than as rows of buttons.
- Tap a line in a Notes transcript on your phone to hear it. Playing the audio back from any point in the transcript worked on the desktop only: recordings kept their audio there and nowhere else, so on a phone the same tap did nothing. Notes now keep the recording on every platform, and an iPhone recording is kept too - that path records through the system audio engine, which produced no playable file at all before. Deleting a note deletes its audio with it.
- Share a note, and get the whole note. Summary, Transcript and Notes each have a share button next to Search, and one press shares all three together rather than whichever tab you happened to be on. Copy does the same thing to the clipboard. If the transcript was translated as you recorded, the translated lines come along in both - the transcript's own Copy button used to drop them silently, so half of what was on screen never reached the clipboard.
- Channels opens your team's chat instead of explaining what a workspace is. Anyone whose active workspace happened to be Personal - which is the default - landed on a full screen of prose about shared workspaces, with the team they were already in offered as buttons to press. Channels only exists inside a shared workspace, so it now resolves that itself and takes you straight to the conversation. The explanation is still there for the one case it describes: you are not in a team yet. It now carries a button that goes somewhere, which it did not when there was no workspace to list, and it is not shown when the workspace list merely failed to load.
- Foxl starts again when it was stuck on "Starting Foxl". The app could sit on its loading screen forever, reporting that the server was not bound while the server was in fact running and answering. One piece of startup housekeeping was failing on a search-index leftover, and because that step ran before the app announced which port it had opened, the window never learned where to look. The announcement now happens the moment the server is listening, so nothing that comes after it can keep the app from opening, and the leftover is cleaned up on the way in. Chat history is untouched by this - search falls back to a simpler match until the index rebuilds itself.
- The startup screen can reach Discord. When Foxl cannot bring the local server up, the problem is usually not one the docs cover, so there is now an "Ask on Discord" link beside the docs one. Both open in your browser rather than replacing the screen you are reading, which used to take the diagnostics and the Restart button with them.
- Older conversations open again. A chat from a month or two back could sit there loading and never arrive. Nothing had been lost - the transcript was simply too large to send: a single web page one tool had fetched was kept in full at 11 MB, and a handful of pasted screenshots came to 13 MB and 18 MB on one message each. Foxl now loads the readable part of a heavy transcript and says where it left something out, so the chat opens in a moment instead of not at all. The largest ones measured here went from 12 MB to under a megabyte. A tool result or an attachment that was left out says so in place, and the full value is still on disk. New chats keep tool output within a sane size from the start, so history stops accumulating this.
- Foxl tells you when it is only looking at recent chats. Asked about something from an older conversation, it would sometimes answer as though that conversation did not exist - it is shown a list of recent sessions, and nothing said the list was only the most recent few. It now sees more of them, knows the list is a window rather than the whole record, and searches your history before saying something cannot be found. The dates on those entries carry the year, so a chat from last year is no longer indistinguishable from one from last week.
- Session records are filed under the right day. A conversation that ended in the evening was saved under the next day's date, which put the list of recent sessions out of order.
- Attached images stop being copied into the offline cache. Screenshots and PDFs were meant to be left out of the on-device copy of your history, and on the desktop path they never were - so the cache held every attachment in full.
- Switching away from a chat on your phone no longer throws the answer away. Ask something, then background the app or lock the screen: iOS freezes the app within seconds, and the reply that was still arriving used to be replaced by "You're offline" - for a turn that had not failed at all. It kept running on our side, and it was still charged to you; there was simply nowhere for the finished answer to go. It is now held for you and appears in the conversation when you come back, whether you return to a still-running app or tap the notification and launch it fresh. A turn that genuinely fails still says what went wrong, and one you stop yourself stays stopped. A short reply now also just finishes: the app asks iOS for a moment to complete the turn instead of being cut off mid-sentence. The held reply is encrypted on your device before it is sent for safekeeping, and the key never leaves your phone - so it is unreadable to us, and it is discarded after a day. As before, we do not keep your message text.
- A finished chat always notifies you now, and the notification quotes the answer. If a reply lands while you are not in the app, the notification is no longer best-effort: it is sent whenever the turn completes without you there. Previously it depended on our detecting the exact moment your phone dropped the connection, so a reply that finished a beat too early arrived silently. Notifications also show the start of the answer instead of "Your response is ready." GPT models were missing this entirely - a backgrounded GPT chat finished with no notification of any kind, and tapping through had no conversation to open. Both are fixed, and every model now behaves the same way. Turning chat notifications off in Settings still turns all of this off.
- Foxl no longer closes itself on Android after you sign in. Signing in asked for notification permission, and allowing it took the app down - every launch after that, before anything drew, which is what "Foxl keeps stopping" was. Registering for push crashed the app outright rather than failing, because the Android builds carry no push credentials; the app now skips that step there instead. Nothing else changes: Android push has never delivered a notification, so there is nothing to miss until the server side exists.
- The two round buttons at the top of the screen are back on Android. The sidebar toggle and the chat menu were invisible - still there, still tappable, just not painted - so the sidebar, search and new-chat had no visible way in. Their icons were being hidden for an iOS-only glass effect that Android cannot draw, and nothing put them back.
- The Foxl logo on the Android boot screen is no longer clipped. The mark was drawn slightly too large for the circle the system masks it to, cutting the corner of an ear.
- The Android app wears the Foxl logo. Its launcher icon, its recents card and its boot screen were all showing the placeholder artwork the Android project was generated with - a light-blue X on a graph-paper circle - which is another vendor's mark, not ours. The boot screen shared the cause rather than being a second bug: Android draws the launcher icon there unless the app names something else, so the placeholder reached the one screen every launch passes through. Dark mode now gets a lighter version of the mark, since the near-black one is invisible on a dark plate, and the app's accent colour is Foxl blue rather than the stock Material pink that a text cursor was tinted with.
- An imported recording can be played back. Importing an audio file transcribed it correctly but never kept the audio, on any platform, so tapping a line in the transcript of an imported note always answered "No audio saved for this note."
- Changing your provider in Settings only takes effect once it saves. Picking one from the dropdown used to switch chat over immediately, so if the save then failed the app was talking to a provider the server had never been told about - visible only as replies from the wrong model.
- Files no longer sits pushed up from the bottom on a phone. It was paying the tab-bar clearance twice, once on a layer that could not use it.
- Foxl Code's setup screen no longer hides its own button on a phone. "Start a task", the last step and the whole point of the page, sat under the floating tab bar with no way to scroll it clear.
- The pet stays out of the way. Three things it was getting wrong: it walked over the message box on a desktop window, so clicks meant for the input hit the animal instead; while the phone menu was open it faded out but still swallowed taps, so a menu row under the invisible pet did nothing; and in the desktop app its walking area was measured from the window's title bar rather than the app header, which pushed it too low. Picking it up and dropping it on the message box now returns it to where it can actually stand.
- Two places showed a code name instead of words. Searching the workspace with no matches printed "workspace.no_results", and a conversation you never titled appeared as "chat.untitled" in titlebar search and in the trash list. Both now read properly, in all ten languages.
- Copy and Regenerate stay on the newest reply after you send the next message. The row was pinned to the last thing in the transcript rather than the last answer, so the moment you asked a follow-up the buttons on the reply you were reading went back to appearing only on hover - and if the new message failed to send or you stopped it, they stayed that way. On a phone there is no hover, so they were simply gone. Both chat and Foxl Code.
- Channels uses the whole width of the window. The messages and the box you type in were held to a 768px column, so on a wide window the conversation was a narrow strip with an empty half beside it. Both now fill the pane, the way every other chat app does. Foxl Agent and Foxl Code chat keep their reading column - a long answer is a document, a channel message is not.
- The workspace switcher appears once, not twice. With a conversation open on a desktop, the workspace name and its chevron showed both in the channel list header and again above the channel name - two controls, one destination, and the second one pushed the channel name onto its own line. On a phone it stays, because the list steps aside there and it is the only thing naming the workspace.
- The "this is the beginning of the channel" block stops taking over the conversation. The full introduction - the large tile, the heading, and the Add people card - is what an empty channel shows. Once there are messages it shrinks to a single line above the first one, instead of ~380px of welcome that has to be scrolled past every time the channel is opened.
- Starting up no longer shows a progress bar that is not measuring anything. The boot screen had a sweeping bar and lazy screens had a spinner; neither had a quantity behind it, since a bundle arriving has no percentage. Both are now the Foxl mark coming into focus, on the same timing as the rest of the app's motion. Under reduced-motion it fades rather than moves.
- The button beside a screen's title is no longer cut off at the top on a phone.
+ New scheduleon Schedules, and the settings button on the notification screen, had their top edge - and with it the top of their rounded corners - clipped by about 4px, so they read as square on top and round on the bottom. The title row now sits 4px lower, which brings the whole button back into view. - Opening a notification on a phone no longer leaves the menu covering it. The bell lives in the drawer, so the drawer was always open behind the notification sheet. Tapping "View all notifications" - or any notification row - dismissed the sheet and left you looking at the drawer, on top of the page you had just asked for. Anything that opens a page now closes the drawer with it. Swiping the sheet away still hands the drawer back, since that means you changed your mind.
- The message bar in Channels sits at the bottom of the phone screen again. It was floating about a tab-bar's height too high, leaving a band of empty space between it and the tab bar. The clearance for the floating tab bar was being applied twice on that one screen, and the extra one pushed the whole conversation up. Foxl Code was unaffected, so the same screen looked right on code.foxl.ai and wrong in the app.
- Foxl Code pages scroll on a phone again. Repos, Work, Inbox, Runtimes, Schedules, Knowledge, Settings and Account had no scroll of their own, so anything past the first screenful could not be reached at all - on Repos that meant every repository below the fold, and on Work every pull request. Dragging did nothing rather than showing a bounce, so there was no sign the page continued. The last row also sat under the tab bar; it now clears it.
- A Foxl Code task branched off a conversation now opens with that conversation. Starting a task from an existing chat used to show a single message bubble - the instruction the orchestrator had written for the agent, drawn as though you had typed it - with no reply underneath and none of the discussion that led to it. The task now opens with the real exchange, and the chat it came from keeps its own copy. This affected every task except the first one started from the home composer, so it also hit each extra agent when one request fanned out into several: those tasks had no transcript stored anywhere, on any device. The synthesized instruction is no longer presented as your words.
- The app reconnects by itself again after the network drops. On a phone or on app.foxl.ai, one brief loss of signal used to put a full-screen panel over everything for the rest of the session: it had no close button, and the condition it was waiting for to dismiss itself could never happen on those builds. The only way out was to force-quit. It now clears the moment you are back online.
- While you are offline, Foxl keeps checking. It used to stop trying and wait for the browser to announce that the network returned - which a phone does not reliably do after a sleep or a change of network, so a connection that came back could go unnoticed indefinitely.
- The reconnect panel can be dismissed, and says what it is doing. A close button, Escape, or a tap outside puts it away; a progress bar shows the wait until the next attempt. Dismissing it does not stop Foxl reconnecting in the background.
- Tapping empty space under a message no longer regenerates the reply. The Copy/Edit/Regenerate row was invisible but still tappable on every phone, so a tap on what looked like blank transcript could silently throw away an answer and ask for it again. In both Foxl Agent and Foxl Code.
- Archive is reachable on a phone, and the timestamp is safe to tap. In Notifications the Archive button only appeared on hover, so on a touch screen the feature was invisible - while remaining live on top of the timestamp, so tapping the time archived the notification with nothing to explain it.
- A failed save now says so. Saving a workspace file, and saving provider settings, both ignored the server's answer: a failure looked exactly like success. Worse, the provider save wrote your choice locally anyway, so the app showed one model while the server used another.
- An outage no longer reads as an empty workspace. When the file list could not be loaded, the page stated there were no files - indistinguishable from having lost them. It reports the failure and offers a retry.
- A chat can no longer hang forever while reconnecting. The reconnect handshake had no timeout, so a desktop that vanished mid-reply could leave the composer stuck.
- On a phone, Foxl Code's message bar is back at the bottom of the screen and the greeting is the thing in the middle. Both had ended up centred together, so the bar floated in the vertical middle with an empty half-screen beneath it. The greeting now centres in the space above a bar docked at the bottom, and the bar clears the floating tab bar rather than sitting behind it - previously the model chip and the send button were partly covered by the glass capsule. The desktop layout is unchanged.
- Foxl Code's model picker has no shaded plate behind it. It had a resting fill that Foxl Agent's model picker does not have, so the same control looked different in the two products. The repository picker beside it keeps its own surface, which is what keeps the two reading as separate controls.
- The message bar is frosted glass again instead of a window onto the chat behind it. It was translucent enough that the transcript scrolling underneath stayed readable through it, so text you had already read sat mixed in with the box you were typing into. Measured on a full conversation, most of the bar's surface changed as messages moved past below it; it is now about an eighth of that, level with the mobile message bar. It still reads as a material - it picks up the tone of what is behind it and the blur is stronger than before - it just no longer transmits legible text. Same in light and dark mode, on desktop and on the phone.
- The Agent sidebar's file browser is called "Files". It was called "Workspace", which is also what a workspace is - the shared space with your members, files and channels in it - so one word named two different destinations in the same sidebar. Foxl Code has always called that screen Files. Settings' "Organization" section is now "Workspace" for the same reason; the old /settings/organization link still opens it.
- Channels now sits at the same distance from the window edge in Foxl Code as it does in Foxl Agent. It is one screen shared by both products, but only Agent gave it the standard page margin, so the channel list on Foxl Code sat 20px further left and pressed against the edge of the content panel.
- Right-click menus are back at their original text size. Menu rows had been shrunk in three steps and then partly raised again; they now read at the size they had through the whole v0.4 series. Rows did not change height and touch targets on a phone are untouched, so nothing moved under your cursor or your thumb. A few of the longest translated labels take an extra line at this size, which is deliberate: the menu cannot get wider without running off the edge of a phone screen, and a second line is easier to read than a row you cannot reach.
- Dark mode is no longer split down the middle. The sidebar and the page beside it were six lightness points apart, so the seam between them was the most prominent edge on the screen and the content area read as the darkest thing in the app. The page canvas now sits one step below the sidebar instead of six, the way every other surface in the set is spaced - so the window reads as one surface with depth rather than a light panel next to a black hole. Text contrast is unchanged and still measured: every secondary label clears WCAG AA on every surface it can land on.
- A two-hourly schedule now reads "Every 2 hours". It used to spell out all twelve times of day ("Daily at 12:00 AM, 2:00 AM, 4:00 AM, ...") - correct, but 128 characters of it, which wrapped a row onto three lines. Any evenly-spaced hourly schedule now states its interval. Schedules that run at specific, unevenly-spaced times still list them, because that is what they mean.
- A page listing paused schedules no longer says it has none. With everything paused, the header advised creating your first schedule directly beside its own "0 of 3 enabled" count.
- The Mac app no longer modifies itself on first launch, so its Apple code signature stays intact. Foxl used to create a link inside its own application bundle the first time the local server started, which broke the seal macOS checks:
codesign --verifyreported the app as altered, and Gatekeeper began refusing the installed copy. The files it needed are now placed correctly when the app is built, so nothing is written into the bundle at runtime. The terminal and image tools work exactly as before. - Tightened the Mac app's requested system permissions. Foxl asked macOS for six capabilities it never used, including a blanket read-write exception for your entire home folder. Those requests granted nothing in practice and are gone; microphone and system-audio recording for Foxl Notes are unaffected.
app.foxl.ainow serves its Apple app-association file correctly instead of answering with the web app's HTML, which is a prerequisite for openingfoxl.ailinks directly in the mobile app.- Removing someone from a workspace now ends their access to its channels. Their access to the app was already revoked, but chat membership was tracked separately and was never cleared, so new messages kept arriving as phone notifications - with a preview of the message text - and
@-mentions of them still returned the full message body. They could also still react to messages and appear as "typing". Eviction now clears channel membership, and every read re-checks the workspace, so this cannot come back through a path that forgets. Direct messages are unaffected. - View-only members can no longer delete files from a restricted folder. Uploading already required edit access; deleting did not check at all.
- Closed a GitHub "Connect" link-forwarding hole. The connect step accepted a session token from its own URL without verifying it, so a crafted invite link could have attached someone else's GitHub installation - and their private repositories - to the sender's Foxl account. The link is now rejected unless the session it carries belongs to the person who created it.
- The "New" line marking where you left off in a channel never appeared.
- A direct message printed the other person's name twice on the same row.
- Tap targets across the app now meet the 44px minimum: the Workspace file tree (12 rows), the product switcher, and the reaction buttons were all smaller.
- Between a phone and a tablet width, Channels showed two panes with nothing selected and no way to open anything.
- Agents, Schedules and For You opened with a large empty gap above the title on a phone - the space for the top bar was being reserved twice.
- Tapping a notification about a channel message did nothing when the app was not already running, and
foxl://links could not reach Workspace, Agents, Schedules or For You at all. - Taps along the top of every phone screen did nothing. The invisible top bar claimed that whole strip, so any button underneath it was unreachable - including the back button in a channel.
- Several screens could not be scrolled at all, so anything below the first screenful was unreachable: For You, Agents, Schedules, Tools, Skills, Logs and Tasks.
- The newest message in a channel, the last file in Workspace, and the bottom of every image, PDF and Office preview sat underneath the tab bar.
- Reacting to a message could leave the emoji picker stuck open on a phone.
- The sign-in screen now sits in the middle of the screen instead of up against the top edge.
- Tapping a tab while the side menu was open navigated away instead of closing the menu.
- An empty Notifications page no longer offers a "Configure Feed Generator" link. The page lists a dozen kinds of notification now, so a single link to one source's settings read as the explanation for the empty screen.
- A chat started on your phone now appears on your desktop right away, and its messages are saved to the phone as soon as the reply finishes. Previously an open desktop window did not learn about the new chat until something else made it reload, and the phone only downloaded the chat for offline reading on a timer that runs at startup - so a chat begun later in a session could sit marked as not downloaded, about messages that were already on the desktop. The desktop also picks up the chat's generated title now instead of leaving it as "New Chat".
Features
- Channels and direct messages: talk to your team inside Foxl. A shared workspace now has chat. Create channels, open a DM with anyone on the team, reply in threads, react, attach files, and search what was said. Messages arrive the moment they are sent - there is no refresh and no desktop app required, so the same conversation is live on your phone, in the browser, and on your Mac. A channel can be open to the whole workspace or private, and a private one is invisible to everyone who is not in it - including its name. Use "Add people" to invite anyone from the workspace. Per channel you can mute it, star it to keep it at the top, mark it read or unread, leave it, or archive it when the work is done
- and reopen it later if it turns out not to be. Files you attach live in the workspace's own Shared files, under a folder per channel, so a file shared in conversation is still findable a month later. Phone notifications for channel messages are on by default and have their own switch under Settings > Notifications, separate from your agent's replies; muting one channel silences that room without turning the category off. Chat needs a shared workspace - a personal one has nobody on the other end.
- Foxl Code keeps recurring work, learned context, and pull-request review in one operational workspace. Schedules can be created, edited, paused, run immediately, and reviewed as a list, calendar, or execution history. Knowledge makes repository memory, agent activity, and the exact context sent to a new task inspectable. When a task opens a pull request, its review rail shows checks, changed files, a line-level diff, and an explicit merge confirmation without leaving the task. The new views are responsive, keyboard-navigable, and preserve tabs and filters in the URL.
- Shared workspaces now look like a drive: one folder per team, and you can choose who sees each folder. Every workspace gets a folder named after the team, and files live inside a folder rather than loose at the top level - so what you are looking at is a place, not a pile. Any folder can be narrowed to specific people, with view or edit access, and a narrowed folder disappears entirely for everyone else - including its name. Anything inside a narrowed folder stays narrowed, so a sub-folder cannot quietly reopen it. Folders you have not restricted stay visible to the whole workspace, exactly as before. Two smaller consequences: a new workspace has to have a name no other workspace is using, because the name is now the folder you and your team will be looking at; and files that were sitting loose at the top level are still there and still work - you just cannot add more.
- Cmd-click a path to your own files and it opens in Finder. When Foxl talks you through your machine -
~/Documents/MyVault,~/Library/Application Support/obsidian/obsidian.json- holding Cmd now turns those into links, and clicking one reveals it in Finder (or your system file manager). Paths inside the Foxl workspace keep opening in the workspace viewer as before. A command that merely mentions a path, likecd ~/Documents/MyVault, stays plain text: it is an instruction, not a destination. Desktop only, since a browser cannot open your file manager. - Drag files straight into a shared workspace. The Shared files panel took uploads only through its Choose files button; now you can drop them anywhere on the panel, and it highlights while you drag. Any file type works - images, PDFs, archives, whatever - and dropping several at once uploads all of them. Dropping a folder is ignored rather than uploaded as an empty file.
- Links to other apps work now. An
obsidian://link to one of your vault notes, avscode://link to a file, atel:number - clicking any of them opens the app it belongs to. They used to render as links and do nothing at all when clicked: the markdown renderer quietly emptied every address that was not a web or email one, so the underline was there and the destination was not. Ordinary web and email links are unchanged, and links that could run code (javascript:,data:,file:) are still refused - that is not something the new setting can turn on. If you would rather Foxl never hand an address to another application, Settings > General > "Open links in other apps" turns it off. - Hold Cmd and the file paths in a reply become links. When Foxl says the bug is in
src/notes.ts:12, holding Cmd (Ctrl on Windows and Linux) underlines that path and Cmd-clicking it opens the file in the workspace viewer - at line 12. Let the key go and the reply is plain prose again, because a paragraph where every path is underlined is a paragraph you stop reading. Only paths written inbackticksare considered, and only ones that really exist in your workspace:useEffectandexit 1are words in a sentence, and a path from some other machine has nothing here to open, so none of them light up. Line and column suffixes are understood (file.ts:42,file.ts:42:7, and a range likefile.py:290-308takes you to its start). A URL in backticks is clickable the same way and opens in your browser. - The Workspace behaves like a file manager now: drag, drop, copy, paste, rename, delete. Drag a file onto a folder to move it, hold Option while you drop to copy instead, and drop into the empty space below the tree to move something back out to the top level. Cmd+C / Cmd+X / Cmd+V work on the selected row, Enter renames it in place, and Cmd+Delete asks before it deletes. Right-click any row for the same actions plus New folder. Files go both ways across the app's edge. Drop files in from Finder to add them, and drag a file out of the tree onto your desktop or into another app to copy it there (desktop build). Anything that would overwrite an existing file asks first, and pasting into the folder something was copied from produces "name copy" rather than a refusal.
- Files and documents can be dragged straight into Shared. Moving something into
Sharedpublishes it to your workspace; dragging it back out makes it local again. A move keeps the document's identity, so its history, its search entry and everyone's access follow it rather than a new copy appearing. - Word, Excel and PowerPoint files are now read and rendered with
officecli, so the agent can actually SEE the document it is working on. The docx / xlsx / pptx skills used to reach for LibreOffice for everything a renderer should do - convert to PDF and rasterize just to look at a page, run a macro to accept tracked changes, run another one to recalculate formulas. LibreOffice is not installed on most machines, so those steps simply failed.officecliis a single self-contained binary that renders the real layout to PNG / HTML / SVG and evaluates Excel formulas itself, which closes the render -> look -> fix loop. Spreadsheet formulas now carry their computed values without any recalculation pass, tracked changes are accepted or rejected directly, and slide decks render per-slide or as a whole-deck contact sheet. Legacy.doc/.xls/.pptand real PDF export are documented as what they are - not supported by the renderer - instead of pointing at a tool that is not there. - Syncing skills now shows you a diff and asks before overwriting anything, and the update can be undone. Pressing Sync on the Skills page previews the change set - every file, with a line-level diff - and writes nothing until you confirm. Applying snapshots the affected skills first, so an Undo button restores exactly what was there before.
AGENTS.mdis now where other agent tools look for it:~/.foxl/AGENTS.md. That path and~/.foxl/workspace/AGENTS.mdare the same file, so a tool that only knows the common convention reads the instructions you already wrote, and there is no second copy to keep in step. Foxl sets this up the first time it starts and re-checks it on every launch, on macOS, Linux, and Windows. If you had already written your own~/.foxl/AGENTS.md, Foxl keeps it - your text moves into the workspace and the two become one file. Only when both files hold real and different content does Foxl change neither and say so, rather than picking a winner. On Windows a symlink needs permission (Developer Mode, or running elevated). Without it Foxl uses a hard link, which most editors separate when they save, so the workspace copy is the one to edit there. If the two do come apart, the next launch puts them back together in the case that actually happens: your writing is in the workspace copy and the home file is still the starter text Foxl wrote, so it re-links onto your writing. It also re-links when the two still match, and steps back only when both sides hold writing of their own. On a filesystem that supports neither kind of link - a FAT32 or exFAT drive, or a home directory redirected to a network share - no link is created and Foxl says why in its log.
Changes
- Deleting a workspace now says plainly that it is permanent, and names the date. The confirmation used to say only that members lose access and that it could be restored for 30 days, which reads more like archiving than deleting. It now states that after the window the files are permanently deleted and cannot be recovered, and it shows the exact date that happens rather than a number of days to count. After you confirm, that date is repeated once more - taken from the server, not from the dialog's estimate. The restore window is 7 days instead of 30. A workspace deleted before this change keeps the 30 days it was promised.
- Right-click menus read at a comfortable size again. They had been taken down to 11px, which is the smallest size anything in Foxl is allowed to be - a floor, not a size to sit on. Menu rows are 13px now. Group headings stay at 11px, so a heading is quieter than the rows under it rather than the same size as them. Row height did not change: menus are still 44px per row on touch and unchanged on a mouse, so nothing moved under your finger. Three menus are wider to match, since a bigger label needs more room before it wraps - the repository menu, the workspace tree's shared-folder menu and the Notes recording menu. In German, Spanish, Portuguese, Russian and French a few of the longest menu labels now take two lines rather than one; that is deliberate, because the width that would keep them on one line runs the menu off the side of a phone.
- "Shared" is now one shelf, and the workspace dropdown is gone. The Workspace page and the Shared skills tab each carried a picker whose first entry was "Personal". Both are removed: everything shared with you - across every workspace you are in - is listed together, and each shared skill names the workspace it belongs to on its own row. The picker looked like it filtered what you were seeing, and did not; what it actually changed was where the next thing you created would land.
- Shared always means shared with people. Creating a shared document, uploading a shared file or adding a shared skill now goes to a real workspace; if you are in none, Foxl offers to create one instead of quietly making something only you can see. Everything already stored under your personal scope stays exactly where it is, still listed and still editable.
Fixes
- A pasted-text highlight stays inside the message bar when you scroll a long draft. Paste something long, type enough after it that the box starts scrolling, and the grey capsule marking the paste slid out of the message bar entirely - floating in the conversation above it, or over the model picker and the send button below. Only the highlight escaped, so it read as an empty grey pill sitting in the middle of nothing. It now stops at the edges of the box, in both Foxl chat and Foxl Code.
- Adding a folder or a file to Shared now takes you to where it went. Typing a name and pressing Add folder appeared to do nothing, and dropping a file on the top level of Shared left "No files here yet" on screen. Both had actually worked: the top level of Shared lists folders, files live inside one, and a create or an upload from there lands in your team's folder one level down - so the result was real but off screen. Shared now opens the folder it put your thing in.
- A file you add to Shared shows up straight away, and deleting one works. Uploading a file to the Shared folder reported success and then the file was nowhere - not in the tree, not after a refresh - and trying to delete one said it no longer existed, about a row that was on screen. The Shared folder in the workspace tree was only ever listing shared DOCUMENTS. Files, which live in different storage, were never in it at all, so no amount of refreshing could show them. The tree now lists both, and delete looks in both places.
- Dragging in the workspace highlights only the row under the pointer. Every file in the folder lit up at once, because the highlight keyed on the destination folder rather than the row - and a file's destination is its parent, which it shares with all its siblings.
- Foxl Notes looks right again: the sidebar rows show their date and duration, and the floating panel has its frosted-glass look back. Each note in the list was cut off just under its title, so the
3h ago - 57:44line under it was sliced in half, and the small always-on-top Notes window had lost its translucency. One line caused both. A housekeeping change added a rule that was meant to ignore the old standalone Notes app, but the pattern also matched the live Notes source folder inside the app - and the styling tool skips ignored folders when it collects the styles a screen uses. So 126 styles used only by Notes were left out of the stylesheet: the ones that let a row grow tall enough for two lines, and the ones that make the panel glass. Nothing failed while this was true, which is why it shipped. - The Cmd+Shift+F hint in the search bar no longer draws its Shift symbol smaller than the others. In the shortcut badge at the right of the titlebar search field, the Shift arrow rendered at about two thirds the width of the Command symbol beside it and sat a pixel low, so the three characters read as mismatched sizes rather than one label. The badge asked for a monospaced font, and the mono webfont ships without the Command and Shift symbols - the browser quietly substituted a different face for those two characters, whose Shift is simply drawn small. The hint now uses the interface font, which designs those keyboard symbols as a matched set.
- "Connect GitHub" in Foxl Code now actually installs the GitHub App. Pressing it moved the browser to code.foxl.ai and stopped there. Two separate causes, both fixed: The connect callback decided which Foxl account should receive a repo grant by looking for an existing GitHub identity on the account - and an account only got one by signing up with GitHub in the first place. Every account created with Google, Apple or a magic link therefore failed the check, which was all of them. The account is now identified by the signed-in session that started the connect, and your GitHub identity is recorded as a RESULT of connecting instead of being required beforehand. This is the flow GitHub documents, and it is no less strict: a connect still has to authorize on github.com, and Foxl still only grants access to installations that authorization actually proves you can see. Separately, the flow always returned you to code.foxl.ai even when you had started it inside app.foxl.ai or the desktop app. Since your sign-in is per-site, that meant landing on a login screen - including when the install had succeeded. You now come back to the app you started from, on the Repos page, with the outcome shown.
- Foxl Code tells you what went wrong when a connect fails. Every failure now has a plain-language explanation and, where there is something to do about it, a button that does it - and the button matches the problem, so a connect that needs a retry offers one while an install that already landed offers Sync instead. Two cases previously showed a raw internal code, and "Link GitHub" could finish successfully and drop you on the chat screen with no confirmation; that now returns to Repos and says it worked.
- Two GitHub responses are no longer reported as the wrong thing. An organization owner still having to approve the install was reported as a malformed redirect, and an install that GitHub could not hand back a correlation token for was reported the same way - both now say what actually happened, and the second one points at Sync, which fixes it in one click. Separately, a GitHub outage while Foxl was asking which installations you belong to used to look identical to "you have none", which sent people to re-install an app they already had.
- iPhone: notifications you got while the app was closed now appear in the Notification Center. The banner arrived, but opening Foxl from the home screen or from the banner itself showed an empty inbox behind the sidebar bell - so a notification you saw on a locked screen and wanted to come back to was simply gone. Foxl reads the iOS notification centre on launch to file those rows, and that read is rejected by iOS until push registration finishes - which on a fresh launch happens after the app's UI is already up. Foxl gave up after the first rejection and waited for either a re-registration or an app resume to try again; neither happens on a cold start (iOS hands over the push token once per install, and a launch is not a resume), so nothing ever swept the stack. It now waits for registration instead of giving up. Tapping a banner also files that notification, marked read, since iOS removes it from the notification centre as you tap - previously the one notification you cared about enough to open was the one guaranteed to be missing from the list.
- The chat list no longer marks a chat that is fully downloaded. On iPhone every row picked up a cloud icon once its messages had synced, which is the state nearly every row ends up in - so the marker said the same thing about everything and meant nothing, in the same spot as the unread dot, which does mean something. Now only the chats you cannot read offline carry an icon: a crossed-out cloud for one that has not downloaded yet, and a filled cloud for one whose copy is behind your desktop. A chat that is fully here shows nothing at all - no icon, no tooltip, and nothing read out by VoiceOver. Its long-press menu still says "Saved on this device" when you ask.
- Skill updates now actually reach an existing install. Sync compared each local file against a repo snapshot it never took, so every difference was read as "you edited this" and preserved - and because every install differs from the repo by one legacy
enabled: trueline, that meant SKILL.md updates could never land. Files under a skill'sscripts/directory were invisible to sync entirely (the scan did not recurse), and a file deleted upstream was never removed locally. All three are fixed, behind the confirmation prompt above.
Features
- Work from Slack: mention @Foxl in a channel and the work happens on your desktop. A session opens on this machine, runs with your real local tools, and the answer comes back as a reply in the same Slack thread. Two top-level mentions in one channel are two separate conversations, and a follow-up inside a thread continues that thread's conversation with its history intact - so "and now do the same for staging" means what it should.
It connects over Slack's Socket Mode, so there is no public URL, no webhook and no inbound port: Foxl dials out to Slack. Configure it under Integrations -> Channels -> Slack with a bot token (
xoxb-) and an app-level token (xapp-, which needs theconnections:writescope). Foxl reacts witheyeswhen it picks the mention up and swaps that for a check mark or a cross when it finishes, so a long job never looks like an ignored one. Allowed Channels is required, and an empty list ignores every mention. Work arriving from Slack runs under this desktop's own tool permissions, which ship permissive by default - so the channel list is the gate, and it defaults to allowing nobody rather than everybody. Mentions that arrive while Foxl is asleep are not delivered; Socket Mode has no queue to hold them.
Changes
- Billing is harder to get wrong under load. Credit debits, request admission, Forge task budgets, storage charges, transcription reports and compute finalization are now atomic and retry-safe, so a dropped response or two simultaneous turns cannot charge twice or charge nothing. Checkouts are bound to the signed-in account, duplicate payment webhooks are ignored, subscription changes apply in the order they really happened, and a paid upgrade waits for the invoice to succeed rather than granting access up front. Refunds and top-ups are matched to the exact order they came from.
Fixes
- Credits you have are credits you can spend. Every request briefly holds credits aside so two turns at once cannot spend the same balance, and that hold was calculated from the largest reply the model could theoretically produce - around 90x a real answer. On the free plan three of the four available models refused the very first message; on Pro the last third of the monthly allowance could not be spent on the most expensive model while the Account page still showed it. The hold is now sized for a realistic reply, and the actual charge was never affected.
- CI is three times faster, which shows up as PRs that stop waiting. Every automated check except desktop code signing and the iOS build moved off a single developer laptop onto a dedicated Linux machine running six checkers at once. The validation gate went from 16.7 minutes to 5.6 - which is now exactly as long as its slowest single check, meaning nothing queues behind anything else.
- The recording pill can be moved out of the way. The floating Recording / Back to Notes button can be dragged with a mouse or a finger and stays where you put it, while a short press still just takes you back to the note. A saved position that no longer fits the window is pulled back on screen instead of being lost off-edge.
- foxl.ai's homepage lines up at every width. The navbar and every section now share one content grid, the local-first security section was rebuilt with a calmer glass treatment, and the phone-to-desktop demo recomposes properly on small screens with reduced-motion respected.
- A schedule runs once, even if two things ask it to. A timer firing at the same moment as Run Now (or a retried webhook) could run one schedule twice and do its work twice. Each schedule now runs one at a time, and a schedule missed while the laptop was asleep is re-armed correctly instead of firing repeatedly or not at all.
- Cron expressions mean what they say, including timezones and daylight-saving changes - the previous implementation only handled part of the syntax.
- Two messages from the same chat cannot trip over each other, while different chats still run in parallel; and Signal's message polling no longer leaves a second reader behind after a reconnect.
- Two things happening at once no longer corrupt each other. Three cases, all reachable in ordinary use: a scheduled agent whose run overran its limit could be started a second time while the first was still going; two uploads replacing the same shared file could publish one file's bytes with the other's size and checksum; and accepting a team invite could consume the invite without actually adding you to the team, leaving nothing to retry. Each is now a single all-or-nothing operation.
- Notes autosave cannot overwrite itself. Two saves landing together could each write from a stale copy of the note, so one lost the other's edit. Saves and deletes are now serialised, a failed save says so instead of failing silently, and a note leaves the list only once its deletion actually succeeded.
- A fast click or a fast retype shows the right answer. Switching agents or changing a Workspace search before the previous request came back could paint the older result over the newer one. Stale responses are now discarded.
- The Code terminal is legible in light mode, using the published Snazzy Light palette instead of plain white, and the mobile Conversation/Terminal switch is back to its compact size while keeping full-size touch targets.
- A failed task reports itself once. There were two banners for one failure, and the embedded Code view added a second scrollbar inside the page.
- foxl.ai leads with the real app. The landing page's synthetic 3D device demo is replaced by a recording of the shipped app doing actual work - turning a CSV into a three-slide deck and opening it in the Workspace rail - with the download buttons, platform links and reduced-motion handling unchanged.
- Renaming a channel no longer wipes its saved tokens. Stored secrets came back from the server masked as
****, the edit dialog put that mask in its inputs, and saving wrote it back over the real credential - so editing a channel's name broke its connection. Secret fields now show as stored and blank means "keep it". - A pasted-text block has its outline back inside your own message. The border and fill were being drawn in the ink colour of the near-black bubble the chat used to have, so on today's light bubble the box was painted the same colour as what it sat on - the title and line count stayed perfectly readable while the box around them was gone.
- The pasted-text highlight reads clearly without adding a colour. Both the capsule in the composer and the block in the sent message are a plain grey wash now, a little stronger than before so the reference still reads as one object. A long paste is something you have already written; it should be easy to see and it should not compete with the text you are still typing.
- A long message can be scrolled again, which on a phone means at all. Paste something big into the message box and everything past the first few lines was unreachable: the box stops growing at a fixed height and the rest was being clipped rather than made scrollable. On a desktop the arrow keys hid it, because a text box follows the caret; on a phone, where dragging is the only way to scroll, the draft simply looked cut off. It scrolls now, the highlight behind a pasted reference follows the text properly, and dragging past either end no longer scrolls the conversation behind it.
- An opened paste is wide enough to read. Expanding or editing a pasted block left it at the width of the small chip, so a long paste was read through a 40-character window where nearly every line wrapped. Opening one now widens it to about 80 characters, and reading it and editing it are the same size instead of two different ones.
- Several pastes open and close together. With two attachments, opening one left a tall card beside a small chip and the row read as broken. They now open as a set, and only the one you clicked to edit gets a cursor.
- "Show in menu bar" works in the installed app. It worked while developing and did nothing in a release: the installed app never read the setting, so the menu-bar icon could not be turned off. It applies immediately now, and a check at build time refuses to ship a settings switch the installed app does not implement.
- Starting a channel that has no adapter now says so. Any unimplemented channel type reported success and wrote "connected" to its record, while the channels list recomputes status from what is actually running and kept showing it as disconnected - two disagreeing answers and no way to act on either.
- Channel errors no longer publish internals to the channel. A failed run replied with the raw error text - provider messages, model ids, file paths - at channel top level where everyone reads it. It now posts a short notice in the request's own thread and keeps the detail in the desktop log.
- Messages from a channel remember the conversation. Every inbound message ran as a cold single turn with no history, so a follow-up answered as though the previous exchange never happened, and a restart silently started a second conversation for the same chat while the first still sat in the sidebar.
Fixes
- The pasted-text chip works in Foxl Agent again. v0.5.12's release notes described a long paste as a tinted chip you could delete with one Backspace, and in Foxl Code that is what shipped - but in the Agent's chat the same paste came out as literal
[#1 ...]bracket text that Backspace chewed one character at a time. The chat page had stopped handing the composer the four things that draw the chip and make it behave as one object. Both chat surfaces now do, and a check that takes 90 milliseconds refuses to let either one drift again. - Linux has a real download link. foxl.ai offered Linux visitors the macOS DMG under the label "Linux build coming soon", even after the AppImage started shipping in v0.5.12. Linux now gets the AppImage, from a fixed URL that does not need editing at each release.
- "Show in menu bar" says what it does, and only does that. It described itself as changing what closing the window does, and it did - closing quit the app with the setting off. That was wrong on both counts: the dock icon reopens a hidden window either way, so the setting is now purely whether the menu-bar icon exists.
Features
- Foxl can be kept out of the menu bar. Settings > General > Show in menu bar turns the menu-bar icon off, and it takes effect at once rather than on next launch. Turning it off also makes the close button quit Foxl: closing the window normally hides it and leaves Foxl running in the menu bar, so with no icon there would be no way back to it.
- Appearance is its own Settings pane, and it does more than pick a theme. Under General you now get theme, UI text size, reduce motion, and an option for hand cursors over buttons. Text size scales the whole interface rather than only the words, so nothing overflows the box it sits in. Reduce motion is in addition to your system setting, never instead of it.
- Language has an Auto detect option. It follows your system language and KEEPS following it - picking the language you happen to be using today is a different choice, and it pins that language for good. A fresh install already behaved this way; now it is visible and reversible.
- GPT-5.6 Sol, Terra and Luna now hold a million tokens. AWS raised the context window for the three GPT-5.6 models on Amazon Bedrock from 272K to the full 1M, so a whole repository, a long contract, or an entire multi-step agent history fits in one request instead of being chunked. Prompt caching still applies at that length, which keeps repeated context cheap. The context meter and the compaction point move with it automatically. GPT-5.5 and GPT-5.4 are unchanged at 272K on Bedrock - AWS's announcement covers the 5.6 generation only - and a GPT model reached over a ChatGPT subscription still serves 272K, because that is OpenAI's own endpoint rather than Bedrock.
- Creating a shared workspace now visibly changes something. v0.5.11 moved shared documents into the Workspace tree and shipped only the server half, so making a team changed nothing anywhere in the app:
Sharednever appeared, nothing could create a shared document, and shared documents were findable by no search. All three now exist. The Workspace tree carries aSharedfolder with a "New shared file" action, the search box searches your team's documents alongside the local ones, and the folder itself opens the workspace's uploaded files. - A scope switcher on Workspace. Personal plus every workspace you belong to, in the sidebar header, deciding which one a new shared file is created in and whose uploaded files the panel shows. It appears once you are in at least one workspace - a dropdown with only "Personal" in it answers a question nobody asked. It does NOT filter the tree: the tree merges every workspace you can see, so a path there always means the same document regardless of this control.
- Upload, download and delete for shared files are back. The panel that does this was complete and was deleted in v0.5.11 only because the page hosting it was, which took the whole feature out of the product. It now lives inside Workspace, on the
Sharedfolder, still leading with what the storage costs and still distinguishing the three billing refusals ("upgrade", "pay the bill", "free some space") rather than reporting one generic failure. - Foxl Code shows your team's shared content too. code.foxl.ai's Files page had none of the above: it read only the orchestrator's private file mount, so a workspace you belonged to was invisible there even after the Agent side worked. It now carries the same scope switcher, the same
Sharedshelf with its documents and uploaded files, and the same New-shared-file action - reading the SAME active scope as the Agent side, so switching workspace on one is the switch the other sees. Shared documents open in place. - Foxl on Android, as a direct download. foxl.ai now offers Android visitors a real app instead of sending them to the browser: the release pipeline publishes a signed APK alongside the macOS and Windows builds, at a fixed URL that keeps working across releases. This is a sideload while the Play Store listing is in review, so the page says as much and tells you Android will ask for permission to install it - and the web app stays one tap away for anyone who would rather not. iOS still points at the browser, which is not an oversight: there is no way to install an iOS app from a link.
Changes
- Settings subheadings have room to breathe. A section title now sits clearly below the panel above it instead of looking like that panel's caption - the same spacing on every Settings pane in all three products.
- Settings > Trash is now Settings > Archived chats. "Trash" described the mechanism; the pane is where a deleted conversation waits for seven days and can be brought back, which is an archive. The notice inside a deleted chat, the link it offers, and the empty state all say the same thing now - they used to point at a name the nav no longer used. Searching Settings for "trash", "deleted" or "bin" still finds it.
- Right-click menus are one size and one system across all three products, and they exist in the places that were missing them. The menu type dropped to 11px with the icon size now owned by the shared component rather than by each call site, which is what had let four different icon sizes coexist inside menus whose text was already uniform. Widths were ad hoc (four values, plus six Workspace menus that passed none at all and rendered at the 128px floor); they are now one value, with one measured exception noted below.
- Foxl Code's task rows can be right-clicked. The seven task actions (open, open on GitHub, copy task id, copy branch, resume, cancel, delete) existed behind a right-click on exactly one surface, the shell sidebar, even though the shared menu component was written to be used by several. Work rows and Inbox notification rows now carry the same menu. The decision queue deliberately does NOT: its two row types are backed by a plan document and a PR notification, and neither carries a task id, so the menu would have aimed Delete at the wrong thing.
- The in-chat Workspace file tree has a right-click menu. Copy path and download were previously reachable only after opening a file into a tab.
- Right-clicking inside a note works. In the standalone Notes app it did nothing at all - not even Copy or Paste - because that Electron host registers no context-menu handler and Electron provides no default one for web content. Both Notes editors now offer undo, redo, cut, copy, paste and select all, each disabled when it cannot act rather than silently inert.
- Repo search on iOS is no longer hidden behind the keyboard. The picker sheet was capped in
dvh, a layout unit that does not shrink when the iOS keyboard opens, so typing a filter pushed the entire result list off screen. - Right-click no longer silently toggles a model favourite. That gesture opens a menu everywhere else in the product, and the only hint it did something different here was a tooltip that never appears on touch. In Foxl Code the star button it replaced was also dead on Pro-locked rows, which is why the hijack had been necessary.
- The animated "..." while waiting for a reply is gone. It was a second indicator for a state the app already showed three other ways, and on a thinking turn it ran at the same time as the "Thinking…" shimmer a few pixels away - two things moving to say one thing, which is what made it read as fidgety rather than as progress. What still tells you a reply is coming is unchanged and, unlike the dots, present in every phase of every turn: the composer's send button becomes a Stop button for as long as the stream is open, a thinking turn shimmers "Thinking…" on its own row, a tool turn shows the running tool with its spinner, and text streams in visibly once it starts.
The empty chat bubble the dots used to sit in is gone with them - it only ever existed to hold them, so leaving it would have traded an animation for a blank grey slab. Screen readers keep the announcement they had (the waiting state is still
role="status", just with nothing drawn), because the Stop button and the streaming text are the two signals assistive tech does not get for free. Applies to Foxl Agent chat, Foxl Code chat, and the agent detail view. - The notification bell moved into the sidebar footer on iOS and the web, carrying the unread count exactly as the desktop titlebar's bell does. Getting to notifications used to mean opening the account menu and picking a row - two taps behind a menu about your account, for the one thing there that is time-sensitive - with a red badge on your avatar as the only hint the surface existed. On a phone the bell takes the "new chat/task/note" +'s slot, which is the control that has alternatives (tapping the Chat row already starts a fresh chat; Notes and Code each have their own New button in the header). On wider screens both fit and both stay.
- A recent search can be removed. Hovering a row in the search field's "Recent" list now shows an X on its right that drops just that entry. A recent is a suggestion, and a suggestion you cannot dismiss is an obligation: one conversation opened by mistake used to sit in the empty state for six more picks before it aged out. Removal touches the LIST ONLY - the conversation itself is untouched, and it is still reachable by searching for it.
Fixes
- A reply that follows a tool run no longer starts with a stray indent. The earlier fix in this release removed the 36px inset from a turn whose only other row was a thinking line, but a turn that actually RAN something still indented every one of its paragraphs - the closing answer included, which is not between anything. So the same assistant began its sentences at two different left edges depending on whether it happened to use a tool. Prose still lines up with the tool rows it sits between; the conclusion sits on the margin every other reply uses.
- Pasting a long block into the composer works, and reads as one object. The paste is a tinted capsule in Foxl's blue - like a link chip - rather than bracketed prose, and the reference number that used to sit inside it is gone from view. Three things that were broken with it: sending did not clear the composer, so after one paste every later message sent the placeholder text again no matter what you typed; the preview could be opened but never closed; and the chevron beside the pencil looked like the expand control while doing nothing at all. A paste at the end of the draft now leaves a space after it, so the next word does not run into the chip.
- The Workspace opens immediately again, and
Sharedshows up for everyone who is signed in. Two separate problems with one visible symptom. Opening Workspace waited for a network round trip to fetch team documents before it would draw the local file list, and that request had no time limit - so a slow connection held back files that were already on disk. The local tree is now served at once and the shared shelf arrives on its own. Separately, the desktop only learned who was signed in as a side effect of choosing Foxl as the model provider, so anyone using AWS Bedrock, a Claude subscription or their own API key had aSharedfolder that could never appear no matter how they signed in. - A reply that did not stop to think is no longer invisible. An assistant turn whose answer arrived as plain text - no thinking step, no tool calls - rendered as nothing at all: the message was saved, the model had answered, and the screen showed an empty gap where the reply should be. It looked exactly like a message that had never been sent, which is why it read as the streaming bug fixed separately in this release. Because Opus 5 thinks by default, the turns that skipped thinking were the minority, so this surfaced as an intermittent "sometimes there is no answer" rather than as a consistent failure. Fixed on the Agent and on Foxl Code together, since both draw chat from the same shared components.
- Cursor's agent mark in Foxl Code is Cursor's actual logo now. What shipped was a hand-drawn stand-in - a grey-to-navy gradient hexagon with three hairlines - that resembled the real cube only in outline. Replaced with the official 2.5D cube from Cursor's brand kit, paths unmodified.
- The context menu now stacks above full-screen covers like the Settings sheet. Its three sibling overlays were raised for this in an earlier release and this one was missed, so any menu opened inside such a sheet would have been unreachable.
- Two menus are wider than the standard on purpose: their labels wrap to two lines in German, Spanish, French, Portuguese and Russian at the standard width. English fits, so this was invisible without switching locale.
- Removed a committed scratch directory whose files contained the Tailwind v3 spelling of a raw CSS variable, which this repo's own notes forbid keeping anywhere the class scanner can reach.
- The app icon goes back to the artwork it had before v0.5.11. That release grew the fox inside the icon's rounded-rect tile from 69% to 82% of it, to sit level with what Apple ships (Maps 84.5%, Notes 84.3%, Safari 80.9%) and with the apps Foxl appears next to in a list. The measurements behind that were right, and the fuller icon is still not the one we want, so the original is restored: the Dock icon is now pixel-for-pixel what it was in v0.5.10 and before. The tile was never the thing that changed - it has been the standard macOS size throughout, so nothing about the icon's footprint moves either way.
- A reply that only thought first no longer starts with a stray indent. When the turn's only other row was a collapsed "Thought for 2 seconds" line, the answer under it began 36px right of the edge every other paragraph starts on. On Korean text there is no capital letter to absorb that, so it reads as a space sitting in front of the first word - which is how it was reported ("흠냐링" with a gap in front of it). The indent itself is deliberate and stays: prose drawn BETWEEN TOOL ROWS is padded to those rows' content column (24px icon + 12px gap) so a batch of commands and the text explaining it read as one thread. The defect was that the padding was unconditional, so a thinking line - a one-line disclosure, not a thread - earned it too. It is now applied only when the turn actually has tool rows. This is also why the gap seemed to come and go: a turn with no thinking and no tools renders through the message-bubble path at zero inset, so the same assistant in one conversation started sentences at two different left edges. Opus 5 thinks by default, which made the indented case the common one. Guarded from both sides, because a one-sided test would have accepted deleting the indent outright: one case measures a thinking-only turn against the bubble path (measured on the old code: 36.0px vs 0.0px, and an absolute-pixel assertion would have accepted 36 as a plausible edge), and a second case measures that prose between tool batches still lines up with the tool rows' content column. Fixed in Foxl Agent and Foxl Code together, since both render from the same shared timeline.
- Your own message now carries the quiet grey the assistant's bubble used to have, instead of a near-black slab. When the assistant reply lost its bubble, the tone that marked "this is chat, not page copy" went with it, and the only tinted object left in a transcript was the user's own high-contrast capsule - so the loudest thing on screen was the message you already knew, while the reply you were actually reading sat untinted. The bubble is now the same
mutedsurface the assistant's was, with normal body ink, which keeps a transcript to one surface colour. - Everything nested inside that bubble was re-toned to match. The pasted-text block, inline code and fenced code were all tinted for the old near-black ground, and two of them became invisible on grey rather than merely wrong: the paste block's fill and border used a near-white token, and code/fences use the same
mutedvalue the bubble now has - measured at exactly 1.000:1 against it, i.e. painted the same colour as what they sit on. Text contrast never showed this (the ink was 18:1 throughout); what had vanished was the surface saying "this is code". All of them are keyed off the ink colour of their own surface now, so they hold in light and dark. - A long paste now reads as an attachment in the message box, not as bracket text. Pasting a log or a config still drops a reference where your cursor is, so the paste keeps its place in the sentence - but that reference is now drawn as a tinted, rounded chip, the way a file attachment looks in a chat app, instead of as 22 characters of literal
[#1 ...]. It also behaves like one object: a single Backspace removes the whole thing, and the arrow keys step over it rather than parking the cursor inside it. - Deleting the reference now deletes the attachment, in both directions. Removing the card above the message box already stripped its reference from the draft; the reverse did not hold. Deleting the reference left the card sitting there relabelled "not placed", and the paste was still sent - ahead of your own words - so a paste you had removed a reference to was still attached, with a two-word label as the only evidence. The two are one object now, and either end removes both.
- A reference whose closing bracket got deleted could swallow the next one. Chewing the end off a reference (which used to be possible one character at a time) made it run on to the FOLLOWING reference's bracket and parse as a single one. The second attachment then vanished from the draft's point of view - its card disappeared while its text stayed - and on send the first paste expanded over the whole run, deleting the words you had typed between the two. Both causes are fixed: a reference can no longer be partially deleted, and a broken one now ends at itself.
- Removing an attachment no longer reformats the rest of your draft. The cleanup after removing a reference ran over the entire message, so it collapsed a deliberate double space, flattened an indented snippet, and trimmed the blank lines off a multi-paragraph draft - anywhere in the text, not just where the attachment had been. Only the spot the reference occupied changes now.
- A duplicated reference is removed everywhere. Copying the reference text left two pointers at one paste, and removing the card took only the first - so the same content was attached to the message twice.
- Pasted text can no longer break out of its own attachment. A paste that happened to contain the marker Foxl wraps attachments in ended the attachment early - which you could hit with no ill intent at all, by copying a Foxl message that already had one and pasting it back. Everything about it was silent: the attachment was truncated at that line so the model never saw the rest of what you pasted, and the remainder left the attachment to be read as your own words, meaning text you pasted as DATA arrived as INSTRUCTIONS. The same hole could manufacture a second attachment that no card in the message box corresponded to. Attachment content and titles are now neutralised on the way out, so one card always means exactly one attachment.
- A shared skill can be edited and removed, not just created. The relay has always supported both -
POST /shared/skillsswitches to update-in-place when given askillId, and archiving isPATCH status: archived- but no UI reached either, so a skill was write-once. Each row now has an actions menu with Edit and Remove. Two things this had to get right. The edit dialog fetches the body: the list endpoint blanks it on every row (otherwise a list would ship every skill's full markdown), so a dialog that trusted the row would open empty and saving would wipe the text. And editing something already published re-sendspublish, because the server recomputes status on that path - an update that omitted it would silently unpublish the skill for the whole workspace with no error. That button says "Save and keep published" for exactly that reason; unpublishing stays on the row where you expect it. Removal asks for confirmation and names the blast radius, since in a workspace it removes the skill for every member including people who already added it. - Settings > Organization is on the design system now. It had the same two defects the Shared tab did. The role pickers were raw
<select>elements (28-36px against the 44px minimum, no focus ring, OS menu) and are the standard Select; the three policy checkboxes were ~13px - under even the 24px WCAG floor, three in a row where a fingertip could hit two - and are Switches with a 44px hit area. The confirmations were nativeconfirm()andprompt(), which cannot be translated or styled and read as the browser asking rather than the app. Both are in-app dialogs. Deleting a workspace still requires typing its name, but the button is now disabled until the name matches instead of accepting anything and reporting a mismatch afterwards. - Settings > Organization is translated. It was ~30 hardcoded English strings including every error, both confirmations and the accessibility labels, and it printed raw wire values (
owner/admin/member, andfailedfor a bounced invite) straight to the screen. All ten locales now carry it. - Shared skills are their own Integrations tab, and can finally be added. v0.5.11 moved them from a sidebar page to a section at the bottom of the Skills tab, which traded one problem for a worse one: the section rendered below seven local skill shelves, returned nothing at all when you were not yet in a workspace, and unmounted the moment you typed in the search box above it. It is now a peer tab - Integrations / Skills / Shared / Channels - with its own
/sharedURL. The part that was actually broken: its empty state read "Publish a skill here and everyone in it can add it" while no publish affordance existed anywhere in the app. The relay has acceptedPOST /shared/skillsall along and the client wrapper was written, but nothing in the UI ever called it, so the copy was instructions for a button nobody had built. There is now an Add button (and one in the empty state), with a compose dialog that takes a name and a SKILL.md body. In a workspace it saves as a draft unless you publish, matching what the server does rather than hiding one of its two outcomes. - The tab's dropdowns are the app's own control again. The workspace picker and the "Share with..." picker were raw browser
<select>elements: 28px tall against the 44px minimum in DESIGN-PRINCIPLES, no focus ring, wrong radius, and the OS menu instead of ours. The "Share..." one also used an option as a fake placeholder, so the browser re-selected it after every choice and the control silently snapped back - it read as an action that had failed. Both are the standard Select now. - Shared skills work with no desktop connected. They live in relay, not on your machine, but the tab was showing the "connect your desktop" preview grid because it sits under the Integrations nav. It now renders for real on the web and on a phone.
- The tab is translated. The whole surface was hardcoded English - 19 strings including both empty states, every error, and the accessibility labels - and it showed raw server values (
draft,archived) as its status badge. All ten locales now carry it. - A member of an open-contribution workspace can share a skill into it. The picker filtered targets to workspaces where you are an owner or admin, but the server only requires that when the workspace turns on "only admins can publish shared skills", so the UI was hiding a workspace that would have accepted the skill.
- The adopt/remove control is a real button. It was a
div, so it could not be reached by keyboard or focused, and it stayed clickable while its own request was in flight. - "From this chat" listed other chats' files. It now knows who wrote what, because the server records it instead of guessing. The panel had no attribution to read, so it inferred: "any workspace file modified since this chat's first message". That has no upper bound, and an old chat can be reopened forever, so the window only widened - every writer sharing the workspace fell into it. Reopening last Monday's chat credited it with a document another chat wrote on Wednesday, the hourly heartbeat's daily memory log, the idle-session sweeper's log for a *different* conversation, and an org document a teammate edited from another machine.
The signal was always there and simply thrown away: every chat turn already runs inside a conversation scope, so which chat asked for a write is known at the moment of the write. That pair is now recorded, including for
exec- the tool that writes almost everything, via shell redirection - by diffing the workspace across one bounded command rather than over the whole chat. Six narrower leaks were fixed alongside it, each with its own cause: - Merely *reading* a file no longer claims it. The filename scan looked at every tool's input and output with no notion of read-versus-write, and
path/fileare exactly the fieldsfile_readand memory search use - so a chat that recalled an earlier chat's notes listed them as its own output, under the stronger of the two internal claims. memory/,Shared/andgenerated/are never claimed by the fallback. They are agent bookkeeping and org-owned documents; a shared document's timestamp is the moment *another person* edited it.- The fallback now has an upper bound - the chat's last message plus a short grace window - so "this chat's files" means written while the chat was active.
- Switching chats no longer shows the previous chat's files under the new one. The conversation id updates immediately but its transcript arrives over the network, so for that gap both guesses ran on the old chat's data. Milliseconds on a desktop, seconds over the tunnel.
- A bare filename in a command is no longer resolved by searching the whole workspace.
cd memory && cat 2026-07-24.mdwas attaching another chat's daily log; an ambiguous name now matches nothing rather than guessing. - A path containing
workspace/is only ours if it really is. Anexecthat inspected a CI checkout (/opt/ci/workspace/MEMORY.md, or a WindowsC:\build\workspace\USER.md) was producing a row for the user's own file. Also: the "new files" badge no longer inherits what you read in a *different* chat - it was keyed on path alone, and the agent reuses stable filenames. Files another chat produced stay fully reachable in the workspace tree below the list. They are no longer *claimed*, which is the whole difference. - An empty
Sharedfolder hid itself, which is why the feature could not be found. The desktop returned no folder at all when a workspace had no documents yet, on the reasoning that an empty shelf reads as "your team has nothing". For a signed-in member of a real workspace that is backwards: no documents yet is the normal first state, and hiding the folder removed the only place from which to create the first one. A signed-out machine still gets no folder, because there is no team to answer for. - Opening a shared document from the in-chat files panel 404'd. That panel resolves bytes through
/api/workspace-raw/<path>, which was pure filesystem with no shared branch - so everyShared/*file failed, silently on the desktop build where the URL is handed to a tag that reports nothing. It now dispatches on the same three-valued path classification the read path has used since v0.5.11, traversal rejection included: aShared/../MEMORY.mdmust not fall through to the local branch, which is the bug that made the classification three-valued in the first place. - On Foxl Code, one unreachable file service blanked the other. Everything on that Files page rendered behind the orchestrator's storage probe - and that probe is negative on every account, because the mode it reports on is switched off in code. So the page was its own "not wired up" notice and nothing else, which also meant the relay-backed shared shelf could never appear no matter how well it worked. The two are independent services now: the private half reports its own state as a note inside the file tree, beside a shared shelf that keeps working.
- Queued messages are a flat list again, not a 3D pile. Each message you typed while Foxl was still replying used to become its own floating card, and the group faked depth to show the order: every card a little smaller and a little fainter than the one below it, overlapping by ten pixels, each with its own frosted surface and its own shadow. Three queued messages measured 736, 706 and 677 pixels wide at 100%, 82% and 64% opacity - so the message furthest from the send box was both the smallest and the hardest to read, for no information you could not already get from its position in the list. They are now rows in ONE panel that sits directly on top of the message box: same width, same darkness, one hairline border, no shadow, and a small return arrow on each row pointing at the box they are going into. Rows are also shorter (32px instead of 40px), so a full queue takes up a third less of the screen. Two things stayed deliberately. The panel still tucks eight pixels under the message box, because that overlap is what makes the two read as one attached surface rather than two boxes with a seam - and tapping the top edge of the message box still puts the cursor in it rather than opening a queued message, which is a real bug this queue shipped once and is now measured on every run along with the flatness itself.
- The Linux and Android CI builds had never once succeeded, because the build machine rebooted itself partway through every run. An account-wide patch policy installed updates on each fresh runner and then restarted it about three minutes into boot - which is less time than any real build takes, so the job died every time (8 of 8 for Android, every run in the repo's history for Linux). Runners now opt out of that patching, which is pointless for a machine that exists for one job and is destroyed afterwards. The Android build is green for the first time - a 31 MB APK, built in 2m 10s on a runner that no longer restarts underneath it. The pool's own smoke test used to pass because it finished in four seconds; it now stays alive long enough to catch a reboot, so this cannot silently come back.
- A message that gets no reply now says why, instead of vanishing. Sending on Opus 5 frequently produced no answer, no error, and no sign the message had ever been sent. The cause was not the model: Bedrock reports a mid-stream failure (throttling, model-stream error) with a frame that carries no
typefield, and the relay forwarded it verbatim. The web client dispatches purely on that field, so the frame matched no branch - not even its error branch - and was dropped. The stream then ended with zero events, which the client finalized as a SUCCESSFUL reply with empty content, and an assistant row with empty content renders nothing at all. So a failed turn looked exactly like a turn that never happened. The relay now translates those frames into a real error event, the way the non-Claude streaming path always has, and reports a stream that dies or produces nothing rather than closing as if it had finished. The client treats a zero-content stream as a failure too, so the turn always ends in something visible. A throttle now reads "The provider is throttling requests. Wait a moment and try again" instead of silence. Opus 5 was hit hardest because thinking is on by default athigheffort, which holds the stream open far longer and gives a mid-stream failure a much wider window to land in. A thinking-only reply is still a real reply and is not reported as an error. - A second message sent while a brand-new chat is still being created is no longer lost. Creating the conversation is a round trip, and a message queued during it was stored against a conversation id that did not exist yet. The code moved the in-flight marker onto the real id but not the queued message, and the queue only matches on an exact id - so the message became unreachable, and the queue is not saved anywhere, so it was gone for good.
- The Notes sidebar now reads as part of the same app as Agent and Code. All three recording-list defects were measured in the browser rather than eyeballed, because the ones that mattered were invisible to a class-name review:
- The secondary text was not legible. The relative time, the duration and the summary preview were painted in a Notes-only alpha (
text-sidebar-foreground/35) that computes to 1.86:1 against the sidebar - rule 7 of DESIGN-PRINCIPLES wants 4.5:1 for 12px text, so it was under half the floor, and the selected-row variant was 3.23:1. Dark theme was 2.9:1 - 4.38:1. They now use--muted-foreground, whose value is 40.6% precisely because that is the lightest ink clearing 4.5:1 on every light surface including a selected sidebar row. The summary badge was 2.08:1 against rule 8's 3:1, and the empty state's icon and copy were short too. - Titles aligned to two different left edges. The summary badge was a flex sibling of the title, so it pushed the title right by its own width - only on rows that happened to carry a summary. Measured 42px against 24px on the row above: an 18px step that appeared the moment an AI summary landed. The badge is now absolutely positioned in the row's own left gutter, so every title starts at the same x whether the badge is there or not.
- It answered "when was this?" in a vocabulary no other list used. The list kept a local copy of the date bucketing that the shared
sidebar-date-scrubmodule was written to delete - it said "Jun 24" where Chat and Code said "June", and dropped the weekday name inside the last week. It also had no inline dividers at all on the desktop, painting the phone's floating pill there instead, and its hand-rolled scroll handler could strand that pill on screen with a stale label (its teardown cleared the pending hide timer, so any list update inside the 1s window cancelled the hide and nothing re-armed it). It now uses the shared module, so all three lists name a date identically. - The list had no bottom-edge fade, the one cue that says "there is more below"; it now uses the same measured
useScrollFadeas the other two. - New
npm run e2e:notes-sidebargate (foxl/apps/web) measures all of the above in a real browser in both themes plus the phone, and compares the Notes row against the Agent row in the same sidebar. 22 assertions. - iOS: a notification you already read no longer keeps its red dot. The app's unread state and the iOS notification centre were two unrelated ledgers, so reading a banner on the lock screen - or swiping the whole stack away - changed nothing in Foxl: the row stayed unread, the dot stayed, the badge stayed. The OS is now the source of truth. On launch and on every resume the app reads the delivered set: anything still sitting there is unread, and anything that has left a set the app had previously seen was dealt with by you, so it is marked read. Rows that never came from a push (desktop notifications, the Settings test button) are untouched, because their absence from the OS list says nothing about them.
- A push that arrived while Foxl was closed now leaves a trace. Only pushes that landed while the app was running were recorded, which is the opposite of the common case - phone in a pocket, three notifications arrive, open the app and the Notification Center said "Nothing here" under a badge that said 3. Those pushes are now filed from the OS delivered list, with the same routing and Open action a foreground one gets. The app also no longer erases your iOS notification centre on launch (it used to clear every delivered notification, which is what destroyed the evidence in the first place).
Changes
- "Shared" is gone from the sidebar; shared things now live where the things they are live. A row called "Shared" organises the app by WHO owns a file rather than by what it is, so the page had to re-implement every kind of thing that can be owned - it shipped a second Skills list and a second document browser, each styled differently from the real one, and the search box on it was a third search that was not the one in the title bar. Shared documents are now a
Sharedfolder at the bottom of the Workspace tree, and shared skills are a section of the Skills page, so there is one Skills UI and one file tree. - Foxl can read shared documents exactly like any other workspace file. This is the part that was missing: the old page could only display them, and the agent could not see them at all. The seam is underneath
readWorkspaceFile/writeWorkspaceFile/getWorkspaceTreerather than in an HTTP route, which is the one funnel both the Workspace page and the agent's own file tools go through, andfile_readnow routes aShared/...path through it - so "openShared/runbooks/oncall.md" works from chat with no separate tool and no second code path. Authorization stays on the relay, which is the only place that knows who else is in the workspace. Two things are deliberately NOT in this release, so they are stated rather than implied: the agent can read a shared document but has no free-form write tool yet (every writing tool constrains its path to a fixed set), and with the old page gone there is currently no UI for CREATING a shared document or running a shared search - both endpoints exist and are unchanged, they just have no caller. Sinceshared_workspace_docsis empty in prod and stg, theSharedfolder stays hidden until the first document is created. Tracked infoxl/TODO.md.
Fixes
- Signing out left a live relay token on the desktop, so the next person on the machine saw the previous user's shared documents. The token is now kept in the desktop database so the
Sharedfolder survives a model switch, and nothing removed it: sign-out only cleared browser storage, and relay tokens last 12 hours. Sign-out now tells the local server to delete it, and the token goes with it across sign-out and restart. The first version of that call could not authenticate itself in a packaged build - it was answered 403 and the token stayed on disk, which worked in development and not in the app people install - so the sign-out request now carries the local connection token the same way file previews already do. file_readstopped being able to read ordinary files. Teaching the agent's one free-form read tool aboutShared/...paths classified EVERY path as shared, so reading any normal file answered "No shared document at ..." instead of its contents. Local paths read from disk again, and shared paths still go through the workspace, both pinned by tests.- A
Shared/../something.mdpath resolved to a LOCAL file. Rejecting the traversal returned "not a shared path", which sent it down the local branch, where it normalised back inside the workspace and passed the traversal guard. Reading it was merely confusing; writing it meant the user believed they had edited a document the whole team could see while a file changed on one laptop. Shared path classification is now three-valued - once the prefix matches, the path is remote and a malformed one is an error, never a local fallback. - A shared document's search result pointed at a page that no longer exists. Every indexed document stored
/shared?doc=<id>as its deep link, and because both Workers answer an unmatched path with the app shell, following one would have been a blank screen rather than a 404. Hits now link to the document in the workspace tree, and the Workspace page honours?file=, expanding the folders on the way to it. - Your usage now resets on your own date, not on the 1st. Credits were already granted on your own cycle - your renewal date if you subscribe, your signup day-of-month if you are on Free - but the "used this period" figure was measured over the calendar month, so the two disagreed and the percentage was the ratio between them. On a Pro plan renewing on the 20th, spending 480 credits before renewal and 5 after showed 49.5% used of 980 credits the day after renewing; the truth was 5 of 500, or 1%. It failed the other way too: on the 1st of a month, someone genuinely 80% through their period read as 0% used. Both are gone. Account now names the exact date your usage resets rather than claiming a month, in all ten languages - the Korean and Chinese copy had been promising a reset on the 1st specifically. If your date does not exist in a short month it falls back to that month's last day and then returns to your real date, so a renewal on the 31st reads Feb 28, then Mar 31.
- Pro credit refills no longer skip a period. Two ways a paying subscriber could be left short, both fixed. A renewal that arrived within a day of an upgrade was discarded by a "granted something recently?" check, so the new period started on the leftovers of the old one. And the backstop that repairs a renewal notification we never received only ran for people sitting at exactly zero - so a subscriber who began a period with 10 of 500 credits left and a lost notification stayed on those 10 for the whole period, with nothing anywhere to refill them. Both now ask the only question that matters: has this period been granted yet?
- The new period now begins at the moment you are actually charged, not at midnight on that date. Since billing runs at a specific time of day, a window that opened at midnight counted up to a day of spending that had really come out of the previous period's credits - on a Pro plan that could read as 45% used when the true figure was 2%, and it stayed wrong for the rest of the period. Free plans are unaffected: those credits genuinely do arrive at midnight UTC.
- The renewal notification from our payment provider was never granting your credits. It carries an invoice, not a subscription, so it does not include which plan you are on - and the code asked it for exactly that, got nothing, and stopped before adding any credits. Every renewal was actually being rescued by a separate safety net that only runs once you hit zero, which is why finishing a period with a few credits left meant carrying those few into the next one. The renewal now looks up your real subscription and grants properly.
- A second plan change never took effect. Upgrades and downgrades were recognised only once per subscription, ever: the first change worked, and every later one was discarded as a duplicate - so a second upgrade was charged without moving your plan or your credits.
- If a long turn ever spent slightly past your remaining balance, Account showed the full amount while the service enforced the smaller one, so you could see credits available and still be refused. Both numbers now agree.
- Subscribers billed on the 29th, 30th or 31st had their renewal date read back from a short month (February shows the 28th) and kept that shorter date afterwards, which started the next period a few days early and over-reported usage. Your real billing day is now remembered.
- Your reset date keeps showing during a billing-provider outage instead of disappearing from Account, and the 80%/100% usage warning emails now fire once per actual cycle rather than once per calendar month.
- Cancelling a subscription now returns your usage window to your signup day. It was still anchored to the billing day of the subscription you had just ended, so spending from your last paid period counted against your new Free allowance: someone who signed up on the 6th, billed on the 22nd, and spent 400 credits before cancelling read 97.6% used the day after their 10 free credits arrived untouched. That figure also decides the 80% and 100% warning emails, so it could tell you that you were out of credits while your balance was full. This affected almost everyone who cancelled, since your signup day and your billing day are rarely the same date.
- The command palette no longer shows a raw
{reset}in the Credits row's subtitle. Naming your reset date in that sentence made it the one searchable description with a value to fill in, and the palette has no date to fill it with, so it printed the placeholder literally in all ten languages. It now reads as a plain "current billing period" there - and Account drops the "resets on ..." clause entirely, rather than trailing off into a bare full stop, in the moments before your reset date has loaded. - A key saved for one vendor could be sent to another. Selecting an OpenAI-compatible provider overwrote the process's OpenAI credential with that vendor's key, and switching back to OpenAI did not undo it - so the next request to OpenAI carried, say, your DeepSeek key. That is a live credential handed to an unrelated company, and it also looked like your OpenAI key had broken. Vendor keys now travel in their own channel and never touch the OpenAI one.
- A key you saved in Settings stopped working after a restart. Only the Anthropic, OpenAI and Google keys were restored on boot, and a provider counts as configured by looking at the environment - so every other vendor came back reporting "not configured" with your key sitting encrypted in the database, and the only way to use it was to re-paste it every launch.
- Requests to a non-OpenAI vendor could go to OpenAI anyway. One of the two places that build a model set the custom endpoint through an option that does not exist, so it was silently discarded and the request went to
api.openai.comwith the wrong key attached. - Turning a skill off destroyed the thing that decides whether it can run. A skill declaring what it needs - binaries, environment variables - had those requirements written back in a form the reader does not understand, so they vanished on the next load. The failure inverted the check rather than merely losing it: a skill needing tools you do not have went from correctly reporting what was missing to claiming it was ready to run.
license,allowed-tools, comments and any other field the writer had no special case for were dropped by the same write. Editing a skill now leaves everything it does not change untouched. - Turning a skill off also froze its instructions forever, and could undo itself. On/off was stored inside
SKILL.md, the one file that syncs from the skills repo. Toggling a skill made its file differ from the repo's copy, which the sync reads as "the user edited this, preserve it" - so that skill stopped receiving updates for good. In the other direction, a skill the repo ships as off that you switched on got switched back off the next time the file legitimately changed. Your on/off choice now lives beside your other preferences, soSKILL.mdstays identical to the repo and updates keep flowing. Both directions are recorded, which matters more than it sounds: a skill you had ever switched off carriesenabled: falsein its own file, and remembering only your OFFs would leave the file with the last word - so the switch would go through, the row would say Enabled, and the skill would quietly come back off. Fourteen of the skills that ship with Foxl declare that same line, so the same would have applied to them out of the box. - Three of the new providers had no icon. GLM, Kimi and MiniMax fell back to a generic server glyph even though their logos were already sitting in the app - the row that maps a provider to its icon file was simply missing, which fails silently because the provider still works. Kimi needed the mapping most: its id is
kimibut the brand file is Moonshot's. - Cost tracking priced models that no longer exist. The per-token table still carried rows for the retired DeepSeek ids and a Fireworks model we no longer offer, while the models you can actually pick matched nothing - and an unmatched model is recorded as costing zero. The dead rows are gone. Spend on third-party vendors is still counted as zero, which under-reports, but that is deliberate: inventing a per-token price would mis-report every request instead, so those rows wait until someone reads each vendor's live pricing page.
- A code block's copy button is now always visible on phones. It was revealed by hover, and a touch screen has no hover, so on iOS the button never appeared at all: it was there, it was full size, and tapping the right spot did copy the code - it was simply drawn at zero opacity, which is indistinguishable from missing. It is now permanently visible on touch and unchanged on desktop, where hover still reveals it. Keyboard users get it on focus too, which was the same gap. This affects every code block in a chat reply on both Foxl Agent and Foxl Code, plus html/svg/mermaid preview blocks and Foxl Code's thinking blocks. A fence written without a language has no header row, so its copy button floats over the top-right of the code itself. That was invisible-and-therefore-harmless before; permanently visible, it landed a bare icon on top of the first line of code and made both unreadable, so the button now carries its own surface there. The surface fades in with the button rather than being painted always, or the block would wear an empty grey pad in its corner whenever the button is hidden.
- The Settings sheet's back button on phones looks like it used to again. Two changes in a row had been made to that one round control in the top-left of the Settings sheet, and together they turned it into something else: first the icon was split so it showed an X at the section list and an arrow only once you had drilled into a section, then its glass was replaced with a recipe that tinted from the text colour instead of the background. The result was a heavy grey disc with a hard ring around it, easily the loudest thing in an otherwise very light header, and it changed shape depending on where you were. It is back to one always-present back arrow in the softer translucent chip the sheet shipped with. Nothing about what it does changed: at the section list it closes the sheet, and inside a section it goes back one level. The now-unused tinted-glass recipe was deleted rather than left behind.
- The app icon looked about 60% full next to every other app; the fox now fills its tile. The rounded-rect tile was always the correct macOS size (824 of 1024 px, the exact standard), but the fox inside it was drawn at only 69% of that tile, leaving a wide empty ring. macOS's own icons draw their glyph at 81-85% of the tile (Maps 84.5%, Notes 84.3%, Safari 80.9%), and the apps Foxl sits beside in a list do the same (Amazon Quick 84.0%), so Foxl read as underfilled wherever icons appear side by side. It was most obvious against a light background, where the white tile fades into the page and only the dark fox registers as the icon. The fox is now 82% of the tile. The tile itself is untouched - same size, shape, gradient and edge, bit-for-bit - so the icon's footprint in the Dock and everywhere else is exactly what it was.
- A reply now uses the full width of the conversation, out to where your own message ends. Removing the bubble from replies left behind the width it used to sit in, so every answer stopped about a fifth short of the right edge while your own message and the composer ran all the way to it - the two sides of the same conversation ending in two different places, which read as an indent rather than as a measure. Measured on desktop, each reply was giving up 140.8px of a 704px row; on a phone, 65.2px of 326px, on the screen with the least width to spare. Replies now end exactly where your message does. This is one shared component, so Foxl Agent, Foxl Code and the iOS app all pick it up together, and the overall reading column is unchanged.
- The chat composer no longer covers the bottom of an open document. It floats over the transcript by design, but it was measuring its position against a container that included the file panel, so it stretched across the panel too and hid the bottom 130px of whatever you were reading - the page just scrolled away underneath it.
- The file panel is no longer drawn over by the app's own header on a phone. The sidebar-toggle chip landed on top of the panel's title and the chat-menu chip on top of its close button, both still tappable, because a
z-indexset inside the shell's content area cannot outrank anything outside it. - Fixed an error thrown on every mount of the workspace editor. Monaco was left to find its own web worker and could not, so it threw
Failed to resolve module specifiereach time a file was opened for editing. It degraded quietly enough that nobody noticed, and no test opened that screen. - The desktop app was marking every chat "not downloaded". The sidebar was told to report sync state on a build that never computes it, and the empty answer read as "none of these are here" rather than "there is nothing to say" - so the one build where your history is unambiguously local was the one build that put a cloud on all of it.
- A cloud no longer flashes on every chat when the app starts. Reading the local cache takes a moment, and until it answered the app assumed the worst about every row. On the phone, where the indicator is always visible, that was the first thing you saw on every cold start.
- Right-click menus are sized for the lists they belong to. The menu text was 14px hanging off 12px rows, so opening it stepped UP in size as you drilled down, and its group heading was the boldest, darkest thing in the popover despite not being clickable. Text is now 12px throughout with a quieter heading. Rows keep their full 44px height on touch.
- A date separator in the sidebar no longer stays up forever once you scroll. Measured on both lists: scroll once and the separators were still fully visible six seconds later. Showing them makes the list taller, and the browser holds the visible rows still by nudging the scroll position - which counts as a scroll, which was read as "still scrolling", which kept them up, which kept the list tall. The loop needed no further input from anyone and re-armed itself about once a second. This shipped in the Agent chat list; it is fixed for both.
- The Foxl Code task list is ordered by the time each row displays. The rows show when a task last changed ("8h ago") but arrived ordered by when it was created, and those disagree constantly because tasks run concurrently and for very different durations - so a job queued last week and finished this morning sat near the bottom under an old date. The relative times now descend properly, and each date group appears exactly once instead of repeating.
- Tapping the top of the message box while messages were queued opened one of them instead of letting you type. The queued-message stack is meant to tuck under the composer's top edge, and it was doing the opposite: the last card was drawn over the composer across that 8-pixel overlap and took the taps there too, so aiming at the input expanded a pending message. It was worst on a phone, on the exact spot you aim at, and only while the queue was showing - which is the whole state the stack exists for. The cards now stay behind the composer while keeping their own order (the one going next is still the crisp card on top). Fixed alongside it: hovering a recessed card was supposed to lift it back to full size so the one under your cursor is always the readable one; it only brightened, and never grew.
Features
- Foxl Workspaces: one shared space across Agent, Code and Notes - with files in it, and a bill you can predict. A Workspace was already the thing that carried shared skills, documents and search to everyone in it; it now also carries FILES, and it is the same tenant Foxl Code means by a connected GitHub account rather than a second, parallel idea of "your team". What changed, and why each piece is the way it is:
- Code's Files and Agent's Workspace are one concept. Files are stored per scope (
personal:<you>ororg:<workspace>) and shared files are visible to every member. The two rosters behind them do NOT merge into one table, on purpose: GitHub is authoritative for who is in a GitHub org, Foxl is authoritative for who is in a Workspace, and neither can be derived from the other (a teammate who signed in with Google has no GitHub identity at all). So a Workspace LINKS its GitHub installations, and a member still needs GitHub's own proof before any repo access follows. - Real documents, not just text. PDFs, images,
.docx,.pptxand.xlsxupload and download intact instead of being mangled as text. Anything that could be interpreted as markup in the app's own origin (.html,.svg) is served as a download rather than rendered, since a shared file is a file somebody else opens. Opening these in place, rather than downloading them, is still to come. - Storage is metered, and the workspace has an explicit owner who pays. Every plan's storage allowance had been declared in the tier table since Foxl Code shipped and nothing had ever measured it. Now: Pro includes 5 GB, Ultra 50 GB, and only what a workspace holds ABOVE its allowance is billed, out of the same credits that pay for model calls (1.25 credits per GB-month). The free plan does not include shared file storage, and an upload that would need it is refused rather than quietly spending a free user's whole month.
- The bill lands on your own renewal day, not on the 1st. Credits arrive on each account's subscription anniversary, so charging storage on the 1st of the month would routinely bill a workspace before the credits that pay for it exist. Consumption is measured daily and charged in GB-months on the billing owner's anniversary (clamped for short months, the way a card does it), so a workspace that held 100 GB for most of a month pays for what it held rather than for whatever happened to be there on one day.
- A failed charge never deletes a file. It pauses new uploads and says so; reading everything you already have keeps working.
- Workspaces can be deleted. Owner only, and recoverable for 30 days. Deleting revokes pending invites and unlinks GitHub installs, but does not uninstall the GitHub App (it is not ours to remove) and does not erase the workspace's audit log (one the subject can erase is not an audit log).
- Four more model vendors, and the model lists behind all of them are honest again. GLM (Z.ai), Kimi (Moonshot), Qwen (Alibaba Model Studio) and MiniMax join the providers you can reach with your own key, and the vendors already listed got their catalogs re-read from the vendors' own documentation. That second half matters more than the first: several ids we were offering no longer exist. Picking DeepSeek used to offer "DeepSeek V3" and "DeepSeek R1", ids that appear nowhere in DeepSeek's current docs, so the request failed the moment you sent it - the picker was advertising models you could not use. Every id now ships with the date its vendor page was read, because these are not guessable: Fireworks spells decimal points with a letter (
glm-5p2is GLM 5.2), and OpenRouter publishes two names per model where only the undated one is accepted. Where a vendor's endpoint could not be confirmed, the entry is left empty rather than filled in with a plausible URL. - Models now say for themselves whether they can use tools or see images. Every third-party model was previously reported as tool-capable and vision-incapable, regardless of what its vendor said, so a vision model looked text-only and a text-only model was offered your tools. Each model now carries the capabilities its own vendor page states.
- Hermes wears the real Nous Research mark. It had been showing a blue circle with an "M" in it - a placeholder drawn in-house, not the actual logo.
- A chat you are reading offline can now be continued right where it is. When your desktop is asleep, opening one of its conversations on the web or the phone shows the saved copy kept on your device, read-only, because a reply belongs to the desktop that holds your files and tools. That was the end of the road: read it, and come back later. The notice now offers a second option - continue here as a new chat - and taking it copies the conversation into a new one on this device and unlocks the message box. The window does not change while it happens: the messages stay exactly where they are, on screen and in place, and the only thing that appears is the composer. The desktop's own chat is untouched, so the original is still there, still on the desktop, still readable offline; the copy is a separate conversation that lives on this device and is yours to reply to now. It stays yours when the desktop wakes up too, rather than being replaced by the desktop's version of history. One thing to know, because it is a real difference and not a detail: the new chat is answered over the relay, which cannot reach the tools your desktop agent used. It is given the words of the conversation, so it knows what was said - but not what was run. If the earlier answers came from reading your files or searching the web, the continuation cannot see any of that and may reach a different conclusion. For work that depends on your own machine, waking the desktop is still the better move. The new chat also does not claim to be waiting for anything. The sidebar marks a chat whose messages have not reached this device yet, and a copy you just made on this device was briefly getting that mark - a small cloud saying the messages arrive when the desktop connects, on the one conversation that needed neither. Rows that genuinely have nothing downloaded still say so.
- Foxl can be built and released for Android now. The mobile app has had an iOS host since v0.3.0 and no Android one at all - the platform folder was never generated, so there was nothing to build. It exists now, committed, and the release path is scripted end to end rather than "open Android Studio and click Generate Signed Bundle": one command produces a signed App Bundle and uploads it to a Google Play testing track as a draft. Verified by building for real - a 31 MB debug APK with the web app inside it, and a 29 MB signed bundle whose signing certificate reads back as the upload key.
Two things the Android host needed that iOS already had, and that a plain platform generation does not give you: the
foxl://deep link, without which sign-in cannot complete at all (the callback has nowhere to land, so the browser sheet just sits there), and the microphone, camera and notification permissions. Both are in the committed project now instead of being a checklist in a README that a fresh setup would skip. Android notifications do not work yet, and this does not change that: the server only speaks APNs, so a phone registers for push and then receives nothing. That is the next piece of work, not part of this one. - The workspace panel is a proper file viewer now: tabs across the top, the whole workspace as a tree down the side, and documents that actually render. The panel that opens beside a chat used to be a flat list of the files that chat had produced, one open at a time, with a back button to reach the list again. Three things were wrong with that and they compounded. Markdown, and every other text file, did not render at all - it showed a card with Open and Download buttons, so the agent's most common output, a .md report, was the one thing the panel could not show you. A .md now renders as a document (headings, tables, code blocks, lists) with a Raw toggle for the source, and source files open in the editor with syntax highlighting. You could only look at one file at a time, so comparing a deck against the spreadsheet behind it meant going back to the list between every glance, losing your place in both - files open as tabs now, up to eight, and closing one lands you on its neighbour the way an editor does. And you could not reach anything the current chat had not made - a file from an earlier chat, or one you put there yourself, needed the separate Workspace page, and a chat that had produced nothing showed an empty panel over a full workspace. The sidebar now shows the whole workspace as a nested tree, with this chat's own files called out above it and marked in the tree so they stay easy to find.
- The workspace panel works on a phone at all. The button that opens it is desktop-only, and nothing in the mobile menu replaced it - so the full-screen mobile presentation, which had been written and wired up, could not be opened. It is a row in the chat menu now. On a phone the tree and the document are stacked screens rather than two unusable columns, back returns to the tree without throwing away the documents you have open, and the tab strip sits above the tree so you can get back to them.
- The cloud beside a chat now tells you what is actually on your phone, and you can do something about it. There was already a small cloud on chats whose messages had not reached this device, but it could only say one thing - "nothing here" - and it said it permanently, on every row it applied to, which in a long list is a column of grey clouds nobody asked for. Three changes. It is now hover-revealed on a computer, so the list is quiet until you go looking, and it does not appear on the desktop app at all, because there the chat history IS on your machine and a sync indicator has nothing to report. On the phone, where there is no hover to reveal anything with, it stays visible - that is the device the answer matters on. And it can now distinguish a third state that was previously invisible: a chat you downloaded earlier that has since GAINED messages on your desktop. That one used to look identical to a fully current copy, so it opened, showed you the text, and quietly left the newest messages out.
- Press and hold a chat on iPhone for the sync actions. Download it to this device, sync a copy that has fallen behind, refresh one that is current, remove a download to reclaim the space, or sync everything at once - the OneDrive vocabulary, on the row it applies to. The menu names the state at the top, so the cloud is explainable rather than just decorative, and every action is greyed out with a reason when your desktop is not reachable instead of failing silently. Swiping still reveals Rename and Delete: a drag goes to the swipe, a hold goes to the menu.
Improvements
- The Foxl Code sidebar reads as two groups instead of one long list, and its task list now tells you when. All six Code rows used to sit in a single block under a "Workspace" heading, which made this the one product whose nav was an undifferentiated run of evenly-spaced rows. They are really two ideas - the work moving through the system (Tasks, Work, Inbox) and the things it runs against (Repos, Files, Runtimes) - so there is now vertical space between the two, and no new headings: two labels over three rows each would be more chrome than the rows they describe. The "Workspace" heading is gone with the split. The task list also picked up the same date scrub the chat list has: scroll it and quiet "Today / Yesterday / Fri, Jul 31 / July" separators appear between the rows, fading out about a second after you stop. They take no space when idle, so the list does not jump. On a phone the same thing arrives as a small floating label at the top of the list, because keeping the inline separators readable needs a pointer resting on the list and a finger cannot do that.
- Restored a Google sign-in credential that v0.5.7 removed by mistake. The v0.5.7 notes describe removing a committed OAuth client secret as a security fix. That was the wrong call: the credential is a desktop-app client, the kind Google explicitly does not treat as confidential because it ships inside every copy of an installed application and cannot do anything without someone approving a consent screen on their own machine. Removing it took away a value nothing else supplies. It is back, unchanged, and nothing needs rotating.
Features
- The Foxl Den is an easter egg now: tap the version seven times. The game used to be a switch labelled "Pixel Office" in Settings > Advanced > Experiments, which meant it was not really hidden at all - it named itself to everyone who opened that pane, and because the command palette builds its settings search from those same labels, typing "game" offered it to people who had never gone looking. So the switch is gone. The way in is tapping the version row in Settings > General > About seven times, the way Android hides its developer options, and seven more taps put it away again. Two other places were saying the quiet part out loud and have stopped: the palette no longer indexes it, and the mobile notification settings no longer list a "Foxl Den" category under a game-controller icon to someone with no game (your preference for it is kept, the row just waits until there is something to notify you about). Your den and everything in it survives locking and unlocking. One thing that used to be broken is fixed on the way past: flipping the old switch did not make the game appear until you restarted the app, because the header read the setting once at launch and never again - now the controller shows up the moment the seventh tap lands.
- Shared workspaces: create one, invite people by email, and the agent is theirs and the team's at the same time. Until now Foxl had exactly one kind of space - yours. Foxl Code had a rough approximation of a shared one, but it was really just the GitHub App installation: the roster, the admins and the identity all came from GitHub, so you could not create a workspace, and the invite path that its database schema anticipated was never built. Now there is a Foxl-native organization. You name it, you own it, and you add people by typing an email address. They get a link, and joining is bound to the address it was sent to, so a forwarded link is not a way into your workspace.
Everything shared hangs off one idea: a scope, which is either
personal:youororg:the-team. Your own space is not "no organization" - it is a scope you happen to own alone, which is why a skill you wrote for yourself can be shared to the whole team without changing shape. What travels with a workspace: - Shared skills. Publish a skill and everyone in the workspace can add it. Whether it is on is remembered per person, so turning one off for yourself does not edit the copy your colleagues are reading. Sharing to the whole organization is admin-gated by default and COPIES rather than moves, so you keep your own version to keep working on.
- Shared workspace documents. Runbooks and notes that belong to the team rather than to one laptop. Any member can write them; skills stay admin-gated, because a skill changes how the agent behaves for everybody.
- Shared search. One search across everything your workspaces have shared, spanning Foxl Agent and Foxl Code. It is scoped in the query itself, so a result from a workspace you are not in is never considered - not filtered out afterwards, which leaks through result counts. Your own chats and local files stay on your machine and are still searched from the title bar.
- Admin control. Who may invite, whether members can publish shared skills, the security posture, and which models the workspace allows. The same identity and configuration carries across surfaces: it is the same workspace on app.foxl.ai, on code.foxl.ai, in the desktop app and on the phone, because it is keyed to your account rather than to a machine. Removing someone takes effect on their next request rather than whenever their session expires, and a workspace can never be left with nobody who can administer it - the guard is a single atomic statement, so two admins leaving at the same moment cannot both slip through.
- A chat that had only synced its title used to be a dead tap; now it tells you what is going on, and fills itself in. On the web app and on iPhone, the list of your chats syncs on every load but a chat's MESSAGES only sync when you open it - so most rows in the sidebar were a title with nothing behind them. Tapping one did nothing you could see: the address bar changed, the row highlighted, and the pane kept showing whatever was there before. Those chats now open and say they live on your desktop and will fill in when it connects, with the composer replaced by that sentence rather than left enabled over an empty history. A mistyped or deleted chat is NOT told that - it would be a promise the app cannot keep.
- And the messages now arrive on their own, quietly. Once your desktop is connected, Foxl downloads recent chats in the background - one at a time, with a pause between each, starting a few seconds after connecting so it never competes with the chat you are actually having, and stopping immediately if the desktop goes away. Rows that are not downloaded yet carry a small cloud in the left margin; hovering it says why. How many chats to keep is yours to set in Settings > General > Offline chat history (off, 10, 20, 50, or all). It defaults to the 20 most recent rather than everything, because on a long history "all" is hundreds of round-trips to your own machine, on a phone, on cellular data.
- Word documents and slide decks now look like Word documents and slide decks. Opening a .docx in the workspace ran it through a converter that maps document STRUCTURE and throws presentation away, so every file arrived as the same unstyled web page - no page, no margins, no fonts, no headers. A .pptx was worse: the text was scraped out of the slide XML and printed as a bullet list, which tells you what a deck says and nothing about what it looks like. A macOS-only picture of page one papered over both, and showed nothing at all on Windows, Linux, or the web app. Both now render properly, in the browser, on every platform: a .docx as real pages at the document's own width with its styles, tables and headers, and a .pptx as real slides with the text, shapes, pictures, tables and gradients where the author put them.
- The + at the top right of a chat is now a workspace-files panel. That slot held a fourth way to start a new chat (Cmd+N, the sidebar +, and the Chat menu are the other three), while the files the agent had just written had no way in at all. It now opens a panel beside the conversation listing the files THIS chat produced, newest first, and previews any of them in place - so the deck and the conversation that asked for it are on screen together. On a phone it opens as a full page instead. New chat is unchanged everywhere it already lived.
- Tool calls now happen where the reply says they happen. An assistant turn drew every tool it used in one stack at the top of the message and then all of its prose underneath, so a reply that went "let me check disk usage" -> command -> "the volume is 93% full" -> command read as a wall of commands followed by text that had lost track of which command it was talking about. The cause was structural rather than cosmetic: a turn recorded the ORDER of its thinking and its tools, but its words were kept as one flat string with no position, so there was nothing to interleave them with. Prose is now an ordered block like the others - live and after a reload, since the ordering is persisted - and a turn renders as the sequence it actually was.
- A batch of commands is one line you can open, not fifteen rows. Consecutive tool calls collapse into a single quiet summary ("Ran commands - 3 steps") that expands on click, the way Codex and the Claude app do it. A run that is still executing opens itself so streaming output stays visible, and then stays however you left it. A lone tool call is still a lone row: wrapping one command in "1 step" would only add a click to reach what was already on screen. Prose between two batches keeps them separate, which is the point - each batch stays next to the sentence that explains it.
- The expand chevrons are hidden until you reach for them. On grouped commands, on individual tool rows and on thinking, the disclosure arrow is revealed on hover or keyboard focus instead of sitting there permanently, so the transcript reads as prose rather than as a column of widgets. The whole row is the click target, so nothing got harder to hit, and touch screens - which have no hover - keep the arrow visible.
- The assistant's reply is no longer in a bubble. Its own words are the content of the page, and a tinted capsule around a long answer interleaved with un-bubbled tool and thinking rows made the reply read as a quoted excerpt inside its own turn. It is now plain text at 80% of the column width - the measure does the job the bubble's edge used to do. Your messages keep their bubble; they are the ones being quoted back.
- The right-hand panel in Foxl Code is now what needs you, not a second copy of your task list. It opened on "In progress" and "Recent" task cards - the same rows, drawn by the same component, that the sidebar on the left was already showing, so it spent a whole column restating what you could see and hid its one unique thing (a plan's subtask board) one click deeper. It is now "Needs you": a queue of the decisions that are yours, each with the button that settles it - approve a plan, answer a question, merge a PR that is green and approved and waiting, look at a build that went red, at a merge GitHub refused, at a reviewer asking for changes. It reads across every connected repo, and when there is nothing it says so instead of showing you a list. The plan board is still there, one click from the plan row it belongs to.
- Two things that need you were unreachable, and now are not. A plan that stopped to ask for approval spawns nothing while it waits - which meant there was no task to select, and the approve button only existed inside a panel that required one. That plan is now a row in the queue with its cost estimate and a Confirm button, no selection required. And the whole queue also renders at the top of the Work page on every platform, so an iPhone - where the side panel has no room to exist - reaches the same rows from the notification that brought you there.
Fixes
- The Settings list is in an order you can predict. It had been sorted by guessed re-open frequency, which put General - theme, language, the version number - at the very BOTTOM of App on the theory that those are set once and left alone, filed Usage under a "Connect" heading next to tunnel pairing, and filed Advanced under "Diagnostics" next to two log viewers. Each of those was argued at length in the code and none of them survives contact with someone scanning the list: frequency is invisible until you already know the app, while category is visible immediately. App now runs broadest-scope-first - General, then Account with Usage directly beneath it (spend is something your account has, not a "connection"), then the panes that decide how the agent behaves, then Trash, and Advanced last, where every desktop platform puts the migration-and-experiments drawer. Opening Settings with no section named lands on General for the same reason. Connect is now only the two panes that wire Foxl to something outside itself, and Diagnostics is only the two log readers. Nothing was removed or renamed, and the phone list follows the same order as the desktop rail instead of leading with Notifications.
- The Usage Explorer sits in a column instead of running edge to edge. It was the one screen in Settings with no side padding at all on desktop (and 4px on a phone) and no width limit, so its text pressed against the pane border and on a wide display the four-up summary stretched to 1390px - far enough that a label stopped looking related to its number. It now uses the same gutter and column cap as the Usage dashboard it drills out of, and as Mobile/Web beside it.
- The daily chart can say a date again. Over a 30-day range it drew one MM-DD label per bar, but a label is wider than a bar at anything under a very large window, so all thirty clipped to "07-..." - a date axis that named no dates. It now labels about eight evenly spaced days, always including the most recent one, and hovering any bar still shows its full date and token count.
- A caption stopped shoving itself out of the page. "Token distribution across models" refused to give up any of its width, so between roughly 768 and 900px - where the Settings nav leaves the reading column narrow - it ran past the page edge. It now shortens with an ellipsis and keeps the full text on hover.
- Signing in to Foxl Notes now looks like signing in. Notes renders without an account (you can read the sample note signed out), so it had no login screen of its own - the signed-out desktop editor reused the same grey "No note selected" placeholder that appears when you simply have no note open: a 14px de-emphasised heading over 12px body text and a small dense button. The one screen standing between a new user and every feature of the product was set two type steps smaller than the equivalent screen in Foxl Code. It now uses Code's sign-in typography exactly - a round bordered icon, a 20px semibold heading, 14px body, full-size button - and the heading names the product ("Sign in to Foxl Notes") instead of just the company, which in a shell hosting three products never said which one you were signing in to. Translated in all 10 languages. The empty-note placeholder keeps its original quiet styling, and nothing changes once signed in.
- The Notes header stops offering buttons that do nothing. With no note open it showed five controls in its top-right corner, two of which could not work there. The transcript toggle was one: the panel it opens only renders for a recording in progress or a note with a transcript, so on the empty screen clicking it changed nothing on the page while still flipping its own label from "Show transcript" to "Hide transcript" - announcing to a screen reader that it had opened a panel that does not exist. The other was the house icon, which clears the selection and the editor and goes to Notes: all three already true on that screen, with no editor even mounted to clear. Both now appear only where they do something - open a note and they are back, because there the house is how you get to a fresh one, and it is also the way back to the editor from Notes Settings. It also draws a plus now: its tooltip has always read "New note" in every language, so the house was promising a different action than the label underneath it. And the "Live panel" button moved up beside Start Recording, where it belongs - it is the other half of recording a meeting, and it had been sitting after the view toggles wearing the only box border in the row, which made a secondary action look heavier than the primary one next to it.
- Two rows in the Foxl Code sidebar no longer say the same word in Korean. Tasks and Work were both "작업" - adjacent rows under one heading, differing only by icon, and because the header title reads from the same string, moving between the two pages changed nothing on screen. Work is now "이슈/PR", which is what the page holds (this repo's open issues and pull requests) and what its own tabs already say. The page heading comes from a second dictionary that had the same collision independently, so that was fixed too - otherwise the duplicate would simply have moved from the sidebar into the title. Portuguese had the same defect in the tool preset picker, where "Default" and "Standard" were both "Padrão": two identical options in a list of five.
- The Inbox row is translated. It was the one nav destination with no translation key at all, in any of the ten languages, so the sidebar fell back to the hardcoded English "Inbox" while the page it opened was translated - a Korean user saw "Inbox" in the list and "받은 알림" once they got there. All ten locales now name it, and a check asserts the row and the page it opens keep agreeing.
- Foxl Code reads in your language, not half of it. The Code pages carry their own dictionary, separate from the shell's, and it shipped with English and Korean only - so a Japanese user got a translated sidebar above a page whose title, tabs, buttons and error messages were all English. That dictionary now ships the same ten locales as the rest of the app.
- A failed code preview says what happened instead of printing its own name. The error state asked for a string that no dictionary defined, in any language, and the fallback for a missing key is the key itself - so when a preview could not render, the panel read "chat.previewError". It now says so in words, in all ten languages.
- The live terminal in Foxl Code follows your colour theme. It was a fixed dark palette - a hardcoded near-black pane with hardcoded ANSI colours - so with the app in light theme it sat in the middle of a white screen as the one surface that ignored the setting, next to two panels directly above it that had always used the theme. Turning only the background white would have been worse than leaving it: the terminal's default text colour scores 1.48:1 against white and its brightest white scores 1.00:1, i.e. invisible, so the whole sixteen-colour set changes with the theme now. Light mode uses a GitHub-Light-derived palette where every colour clears the 4.5:1 contrast bar on white (yellow becomes a brown - a terminal yellow cannot be read on white, and the agent uses it for budget warnings); dark mode keeps the colours it always had, moved onto the app's own card surface so it matches the panels above it. Text selection now has a colour in both themes - it had none, and the built-in default is white, which was invisible the moment the pane went light. Switching theme with the terminal open repaints it in place, keeping the scrollback and the live connection. The "session stopped" card over a settled terminal was also a hardcoded black fill behind theme-coloured text, so in light mode it was near-black ink on a near-black card; it uses the theme now too.
- code.foxl.ai serves Foxl Code and nothing else. That host is the Code product alone, but five addresses still reached the Agent super-app's Integrations screen -
/skills,/tools,/channels,/systemand/customeach kept its URL and rendered a page full of controls for software that host does not run. The router recognised those five names before it checked which host it was on, so the check that sends every other foreign path to Tasks never got a turn. It now runs first, which also means the next address added cannot slip past it. Three more doors are shut with it: a foreign address's trailing part is no longer adopted as a task id (/chat/<conversation>became a task URL naming a conversation, and/billing/successa task called "success"), Cmd+N in Foxl Code starts a new task instead of jumping to an Agent chat, and the fully-qualified spelling of the host (code.foxl.ai., with the trailing dot, which really does resolve) is recognised as the Code host - it was not, so that one address served the entire Agent+Notes app, product switcher and all, and stopped passing GitHub's webhooks through to the orchestrator. - The + at the bottom of the sidebar makes the thing you are looking at. In Foxl Code it created a chat instead of a task, and in Foxl Notes it threw you out of Notes into a chat - one control, hardwired to the Agent product, in all three. It now starts a task in Code, a note in Notes, and a conversation in Agent, which is what Cmd+N already did; the button and the keyboard had disagreed, and the button is the one you can find. Its tooltip says which of the three it will do, so a screen reader no longer announces "New chat" on a control that makes tasks. Pressing it from Repos, Files or Runtimes lands on the empty task composer rather than doing nothing. One of those doors was a CLICK rather than an address, which is why trying URLs never found it: opening a notification about a schedule, a chat or a finished agent put the Agent page for it on screen, because a notification's destination comes from a table that maps most kinds to Agent pages. Every in-app navigation now refuses a destination this host does not serve, so a control nobody has thought of yet cannot leak either.
- code.foxl.ai no longer warns you that a desktop you may not own is offline. The sidebar header carried the relay-tunnel status dot, and its popover said "Desktop Offline - Chat-only mode. Connect desktop for full features - Go to Settings". None of that describes Foxl Code: it runs its work in AgentCore through the orchestrator, never calls the desktop tunnel, and has no chat-only mode - so the warning was permanent, about unrelated software, and its one action opened a pane that cannot pair anything from that host. The dot and its popover are gone there; app.foxl.ai, where the tunnel is real, keeps both unchanged.
- A Foxl Code tab says it is Foxl Code. One bundle serves both app.foxl.ai and code.foxl.ai, and the page title was the static "Foxl" both got - so with both open there were two identical tabs, two identical window-switcher entries, and bookmarks that could not be told apart. The Code host now titles itself "Foxl Code", including on the sign-in screen.
- The chat menu no longer comes up as a grey slab, and the "..." now turns INTO it. Two things were wrong with the menu behind the round button in the top-right of a phone chat. It sometimes rendered dark - a flat grey panel instead of glass - and it depended on your PHONE's Light/Dark setting rather than the app's, which is why it looked random: the chip and the rows were hosted in a native glass container, and that container was the one surface never told which appearance to draw for, so it followed iOS while everything inside it followed Foxl. Same build, same app, only the OS setting changed: the panel measured 151-159 grey with iOS in dark mode and 243-253 with it in light, over a white page. It also left the round chip sitting on the panel's corner the whole time it was open, as a small grey hump. Opening now MORPHS: the circle dissolves as the square panel grows out of the same corner, and closing runs it backwards - the panel shrinks back into the circle's place and the circle returns. One shape at a time, and the material is the app's own in either theme.
- The Logs console showed its own HTTP traffic and almost nothing else. It now shows what the server is actually doing. Three separate causes, each measured against a running server:
Every request the app makes to itself was logged at the same level as real output, and the buffer held 1000 lines. Ordinary background polling was enough to fill it: after a few hundred routine reads the buffer measured 100% access logs and zero lines from anything else - every boot, database, scheduler and skills line evicted by traffic nobody asked to see. Routine reads are now recorded as
debug, so they no longer decide what survives, and the buffer holds 5000 lines. Nothing is hidden: a new Debug filter shows them, with counts. The capture that feeds the console installed part-way through the server's startup, so every module that logged while initialising was lost.[DB] SQLite database initializedwas verified reaching the terminal on startup and never the console, one line above a line that did arrive. Capture now installs before anything else loads, and that line is in the console. The agent SDK's own logging was discarded before it reached us. Its default logger definesdebugandinfoas empty functions, which silently dropped 58 places where it reports what it is doing - loading skills, offloading tool results, evicting context, model and session internals. It is now wired to ours and appears under aStrandssource you can filter to or ignore. Also in the console: Info and Debug filters (it offered only All / Errors / Warnings, while counting info lines it never displayed), aDBGlabel instead of debug lines mislabelling themselvesINF, source chips built from what has actually arrived rather than a one-shot probe taken on open (so a source that first logs later gets a chip, and none are dropped past the eighth), and batched rendering with a row cap so a chatty agent no longer re-renders the whole console once per line. - Settings no longer draws a box inside a box. Appearance and Language each had a full-width bordered panel of their own whose entire content was one small control, so the General pane opened on two large mostly-empty boxes, each holding a smaller box. They are now two rows of a single "Display" panel, with the name and its explanation on the left and the control on the right - the layout the Notes settings have used all along. The Light/Dark/System switch also stops stretching: it was written as a block-level flex row, so its track ran the full width of the panel while the three buttons huddled at the left edge. It now hugs its own labels, and its selected segment is the raised light chip on a tinted track that the product switcher and every filter bar in the app already use, instead of a filled button that looked like it came from a different design. On a phone each control still takes its own full-width line with 44px targets. Sweeping the rest of Settings for the same shape turned up three more: Task Budget was a 608px box holding one 128px dropdown pinned to its right edge (it printed its own name twice, and removing the duplicate had left the row's left half literally empty) - it is now a normal row with its name and explanation; the Chrome extension panel drew an empty bordered strip once the extension connected, because its only content was the install steps it no longer needed to show, so the box now goes away with them; and Notes' "Live translation" panel titled itself and its first row with the same words in seven of ten languages, while its auto-update row was the one setting name on that page rendered grey - the same colour that page uses to mean "disabled" - next to a live switch.
- The Foxl Code sidebar says which product you are in. The tab title was the only place that name appeared. code.foxl.ai serves one product, so it does not draw the Agent/Notes/Code switcher - and that switcher was the only thing in the header naming what you were looking at, which left a bare fox mark and chrome indistinguishable from app.foxl.ai's. "Code" now sits beside the mark, as part of the same Home button rather than a label next to it, so the whole thing is one target and both halves take you home. The collapsed icon rail, where there is no room for a word, shows the mark alone.
- Dropdowns on a phone could open taller than the screen with no way to scroll them. Every Select and every long-press context menu in the app capped its height through a Tailwind v3 spelling of a raw CSS variable, and v4 compiles that to an invalid value the browser throws away - so the cap was not merely wrong, it was absent. The list then grew to fit all of its options, which also stopped the
overflow-y-autobeside it from ever engaging: measured on an iPhone with 21 options, the menu ran 221px below the fold, seven options sat off-screen, and the list could not be scrolled at all. Six more components carried the same mistake, including the chart legend, where it was killing the swatch colour. The same spelling in comments and docs was ALSO compiling into the stylesheet - v4 scans file text, so writing the broken form down was enough to ship it. - Menu and dropdown rows are now a real touch target. Options in the Foxl Code model pickers, and every row of a long-pressed context menu, were 32px tall - 12px under Apple's minimum - on the controls that choose which model runs your coding tasks and, on Repos rows, the only path to "View on GitHub" and "Copy clone URL". They are 44px on a touch device and unchanged with a mouse.
- The task header on a phone hid its own controls off the side of the screen. Open a task with a red build, or one with a pull request and an issue, and the header row grew past the viewport: Conversation/Terminal, the PR and Issue links, and the Cancel / Retry / Delete / Reset-session menu were all beyond the right edge, with nothing that could bring them back - so a phone could not cancel a running task, and Terminal, the only view of what the agent is actually doing, was unreachable on exactly the tasks you open in order to look at it. The row now scrolls sideways and reaches its end, the task's name no longer collapses to nothing to make room for status chips, and every control in it is 44px.
- Inbox rows can settle the thing they are about. A row that said a pull request was waiting on you offered only "Open PR", which moves the work to GitHub rather than doing it - you left, merged there, and came back to a row that still looked unresolved. A row for a PR the agent opened now carries Merge itself, using the same merge the Needs you queue and the Work page use, so the three surfaces cannot disagree about what merging does. It takes two presses, because merging writes to a default branch and this is a list you scroll and swipe through; the armed state relaxes after a few seconds. Rows whose event never carried a PR number keep the link rather than guessing which pull request to merge, and a repo you no longer have installed shows no button at all.
- Foxl Code pages start at the top of the screen. Every contained Code page (Work, Repos, Runtimes, Settings, Account) had up to 40px of empty space stacked on top of the header the shell already draws, so the title read as floating in the middle rather than beginning the page - most of a phone's first screen went to nothing. The gutter at the sides and the bottom home-indicator inset are unchanged, and so is every type size.
- The Repos header lines up with its own buttons. The title sat 4px low on a phone against the Sync and Connect buttons beside it, because a heading carries more leading than a 36px button and the row aligned their box tops instead of their centres.
- Repos does not lead with a paragraph about tokens. The GitHub-App guarantee (a fresh repo-scoped token per task, no personal access tokens) was a bordered panel above the repo list, and before that a copy of the same text on every repo card. It is true and it never changes, which is exactly why it does not need restating on every visit ahead of the list the page exists for.
- Inbox puts "Read all" and "Clear all" on the title row. They sat on the filter row opposite the Needs-you / All toggle, which reads as though they apply to the filter you are looking at. They do not - both act on the whole feed - so they now sit with the page title, with Clear all last.
- The Needs you panel can be reopened on a narrow window. Its reopen control used to appear only above 768px, while dismissing the panel below that width was remembered across sessions - so a small window could lose it with no way back.
- Foxl Code has an Inbox: one screen for every repo. Work has always been per-repo by construction - it needs a repo selected and refuses without one, because the GitHub token it borrows is scoped to exactly that repo - so "what needs me right now" could only be asked one repo at a time, by changing the picker and re-reading three lists. Inbox answers it once, across all of them: every PR waiting on you, red build, budget warning and hard-capped agent, each row naming its repo, with a Needs-you filter separating the things stuck on a person from the things that merely happened. It opens on the sidebar next to Work, and old
/activitylinks land here now - that URL used to go to Runtimes, which only lists agents that are still running and so could never show you anything that had already finished. It also costs nothing to load: the history was already on the server, so the page reads it back rather than asking GitHub again, which is what makes it safe to open on a phone on cellular. If a push notification arrives while the app is closed, the event is here when you open it. - The Settings list has its icons back. They were removed on the reasoning that fifteen glyphs in a narrow rail competed with the labels for the same glance; in use the rail reads as a list of places, and the leading glyph is how a row is found without reading it - which is also what the phone's grouped list does, so keeping them means the two sizes agree instead of differing by breakpoint. One thing the removal had noticed is kept: General and Web Access were sharing the same globe, so two rows could not be told apart by their icon. Web Access has its own now.
- The Notes screen on a phone drops two controls that were not controls. The word "Notes" at the top sat inside a filled grey pill, because the header's Notes / Background / Report tab row is desktop-only in its other two entries - leaving the phone a "tab bar" of exactly one tab, whose selected state was advertising a choice you could never move. It is plain text now, like every other page name on a phone. The house icon next to Search is gone from that screen too: it clears the selection and returns to Notes, all of which the home screen already is, so tapping it did nothing while holding one of four slots in the row your thumb reaches first. It stays for an open note, where it is the way back. Two things that were quietly broken behind that tab came out with it - tapping "Notes" on an open sample note now really opens the editor instead of re-showing the report, and "Add context for better AI" opens a Background screen (with a Back button) instead of being a tap with no effect at all.
- Foxl Code's sidebar highlights one thing at a time. Opening a task lit up both the task and the "Tasks" heading above it, so the sidebar showed two selections and neither one told you where you were. Now picking a task moves the highlight onto that task and the heading steps back - the same way Foxl Agent has always behaved when you open a conversation under Chat. Clicking "Tasks" still takes you back to the new-task screen and takes the highlight with it.
- The "1" on Chat now tells you WHICH chat. When a reply landed in a conversation you were not looking at, the sidebar's Chat row put a number on itself and stopped there - so you knew something had finished but not what, and the only way to find it was to open chats one at a time until the number dropped. The conversation that got the reply now carries a dot of its own, in the empty gutter at the left of its row, and clicking it clears both. The count and the dots are the same underlying set read two ways, so they cannot disagree. Also fixed alongside it: replies produced by finishing background agents never marked anything unread at all - the one kind of completion you could not have witnessed was the one that left no trace - and deleting or archiving an unread chat left its marker behind forever, counting toward a badge with no row left to click.
- The empty chat on a phone is the suggestions, centered, and nothing else. It opened with three lines of text nobody asked for above the part you actually tap: a headline ("Ready to dive back in?"), a label announcing the cards underneath it ("Try asking something like:"), and a legal notice, with the whole stack pinned to the top and a screen's worth of blank space below it. The headline was the worst of them - it was written by the model on every open, so the largest text in the product was a different sentence each time and none of them told you anything; the desktop had already stopped generating one for that exact reason. All three are gone, the cards now sit in the middle of the free space between the buttons and the composer, and they line up with the composer's edges instead of being inset further than everything else. The AI disclosure moved to Account, where it stays put - on the zero state it vanished the moment you typed, which is the whole conversation it was describing.
- The Settings sheet's close button is now a glass button you can actually see. It already carried the shared glass chip recipe - byte-for-byte the same blur, tint, rim and shadow as the round buttons in the chat header - and was still effectively invisible: measured on an iPhone 15 Pro viewport it stood 2.5 luminance levels off the panel behind it, where Apple's own round system button stands about 25. The reason is that glass is defined by what it refracts, and this one sits on the sheet's own near-flat surface, so there was nothing behind it to lens; the chat buttons look right because on iOS they are swapped for a real system material, which a control inside a sheet cannot use. It now has a recipe meant for a button sitting on a surface rather than floating over the page, keeping the same shape, rim and depth while taking its fill from the text colour, so it reads in both light and dark mode - 22.7 and 28.8 levels measured - and deepens instead of fading when pressed.
- The Foxl Code message bar sits where Foxl Agent's does, and its two pickers read as two controls. Three separate things on that one bar. The card sat lower than Agent's because Agent's composer pays the home-indicator inset (
max(1rem, safe-area)) and Code's paid a flat 16px - so on a phone that reserves 34px, Code's bar was inside the system gesture strip. The model name was clipped to "So..." whenever a long repo was selected, because the two pickers competed for width as equals while the repo trigger's name refuses to shrink: measured at 393px withagentcore-ws-validation-0721selected, the repo control took 209px of a 359px row and left the model 30px for text that needs 59. And the repo name did not merely clip - it painted 52px outside its own button, over the control next to it. Now the model name never pays first, it falls back to a labelled icon (keeping its name in the tooltip) only when the row is genuinely too narrow for any two names, and each picker sits on its own surface so the eye can tell where one ends and the next begins. - A Foxl Code task on a phone says its name once, at the top. The header showed the section word "Tasks" and the strip below it repeated the task's own name, so the first 104px of the screen spent a full row on a word that never changes and squeezed the status, the Conversation/Terminal switch and the task menu into a 48px band under it - where the status chip was already truncating to "T..". The task name moved up into the header at the header's own size and the strip keeps just the controls. Desktop is unchanged: there the two rows read as breadcrumb-then-controls and there is width for both.
- Connecting GitHub from the iPhone app no longer dead-ends. Pressing Connect took the app itself to the sign-in handshake, which meant leaving the app with no address bar and no back button - and because the app does not share Safari's session, most people landed on a GitHub login screen they could not get past or escape from, which read as "Connect goes to code.foxl.ai and nothing happens". It now opens in the system browser, the way signing in to Foxl already did, so your existing GitHub session is there and the app is still behind you when you finish.
- The repo you are working in is no longer abbreviated away. The repo button clipped from the right, which is the half that names the repo - so several repos in one organization all showed as the same truncated string, and the control could not answer the only question it exists to answer. It now keeps the repo name whole and gives up the organization prefix first, and the list you pick from shows full names instead of trimming them too.
- Repos you use most are at the top. The picker was alphabetical, so a repo you had not touched in a year outranked the one you were in five minutes ago. Recently used ones now sort first, under their own short "Recent" group, and the rest stay alphabetical for when you are hunting a name. If you lose access to the selected repo, Foxl falls back to your most recent one rather than to whatever sorts first in the alphabet.
- Less chrome above the thing you came for on mobile. Work opened with a decorative icon over the word "Workspace" - the name of the sidebar group it sits in - above the title "Work", and Repos opened with "Connections" above "Repos"; both then repeated the page name a second time in the header bar. Those are gone, and Work's refresh button sits beside its title instead of across the screen in the corner. The last row of every Code page also clears the iPhone's home-indicator strip now, instead of sitting underneath it.
- The GitHub App installation row shows the GitHub mark. Small enough to be worth checking: at the sizes used inline the outline glyph reads better, so the filled Octocat is used only in the one slot large enough for it to be recognizable.
- A dialog on a phone could be taller than the phone. The Schedules window measured 904px inside a 659px screen and was centred on its own height, so it hung off the top AND the bottom at once: "Create Schedule" sat above the top edge, Cancel and Create below the bottom one, and nothing inside was scrollable - so the form could be filled in but neither identified nor submitted, at any scroll position. Every dialog in all three products now caps its height against the visible viewport and scrolls its own overflow. 26 of the 33 dialogs in the app passed no height limit of their own, so this was a property of the shared component rather than of that one window.
- Foxl Code printed the page name three times. On a phone, Work showed "Work" in the app header, "WORKSPACE" as an all-caps eyebrow, and "Work" again as the page's own heading - so half the first screenful restated where you already were. Repos and Runtimes did the same. The Agent side had solved this a while ago and the Code pages were simply not on the list; they are now, they share one header component instead of three hand-rolled copies, and the eyebrow is desktop-only where there is room for it.
- Sections on a phone sat further apart than they should have. Schedules and Agents were spending desktop-sized padding between groups on a 393px column, and the hero's buttons had their own line - which put them between a heading and its own subtitle and read as a floating toolbar over an empty half-row. The buttons now sit on the heading's line where the heading's own size does the separating, and the padding is trimmed for the narrow column. Counters move below the summary line on a phone rather than competing with the title and the buttons for the same 393px.
- Workspace told you to select a file when there were no files. The empty workspace showed "Select a file to view and edit" - and on a phone the file list is the whole screen when nothing is open, so the only thing on screen was an instruction you could not follow. It says what is actually true now.
- One line in Settings was tighter than the app's own readability floor. The "model does not support task budgets" note was set at 1.43 line-height where prose requires 1.5.
- The Foxl Code chat scrolls on a phone. Its transcript could not be dragged through history on a touch screen - and on desktop it was fine, which is why it survived. The two chats had grown two different transcripts: Foxl Agent's chat owns its scroller and stops auto-following the moment you scroll UP, whichever way you scrolled; Foxl Code's went through a stick-to-bottom library where a finger cannot get out of "keep pinned to the newest message" while a reply is streaming - one of its two escape hatches needs a mouse wheel, and the other is switched off on exactly the frames where the answer is growing. So the view was dragged back to the bottom on every reflow and history was unreachable. Foxl Code now renders the same transcript component as Foxl Agent - one implementation, not two that look alike - so scrolling, following the stream, releasing at the bottom, and the keyboard behave identically in both products. A regression test drives a real touch drag through both chats so this cannot come back.
- The two chats are now the same chat. Beyond the transcript, the message bubbles, the tool-call rows, the image viewer and the composer were each two near-identical copies, and the copy that got a fix was not always the copy the product rendered - which is how the scroll bug survived in the first place. They are one implementation each now. Two fixes crossed over as a result: Foxl Code's Stop button works on a phone (iOS drops the click when the same tap dismisses the keyboard, so it needs the touch path Foxl Agent already had), and a Foxl Code reply containing an html, svg or mermaid block now previews it instead of printing the source. Foxl Agent keeps everything it had, including the live streamed output of an exec tool and the auto-expanded row when a coding agent starts - both of which came the other way, from Foxl Code.
- Opening Foxl Code on a phone no longer throws the keyboard up. Every time you opened Code it put the cursor in the chat box, so iOS raised the keyboard over half the screen before you had decided to type anything - on the screen you came to read, with no gesture to dismiss it other than tapping away. Foxl Agent's composer has skipped that on touch devices since the mobile pass; the Code composer was forked before that landed and kept taking focus on arrival, on every thread switch, and again each time a reply finished. It now waits for you to tap it, which is also the gesture iOS honours most reliably. Desktop is unchanged - the cursor is still in the box when the page opens, so you can start typing immediately. An iPad was affected twice over, because iPadOS Safari reports a desktop Mac browser string and fell through to the desktop path; it is now detected by its touchscreen.
- Foxl Code's Save button follows you down the settings page again. Almost everything on that screen - instant spawn, auto-merge and its options, the autonomous loop and its whole sub-tree, the budget fields, custom instructions, the MCP connectors - is held locally until you press Save, and the button had been moved into the normal flow of a page that is about eleven hundred pixels tall on a phone. So you would flip a switch near the top, the confirmation would be several screens below, and a flipped switch that has not been saved looks exactly like one that has. It is pinned to the bottom of the page again, on every screen size, without the two problems that got it unpinned: it no longer stretches past the page's margins (which had left its top edge stopping short on wide windows) and it no longer adds its own gap above the home indicator on top of the one the settings sheet already adds. The notification switches on the same page still save the moment you tap them - those go to a different service and never needed the button.
- The round glass buttons on iPhone are real glass again. The circular controls at the top of the screen have been drawing themselves in CSS since v0.5.8 instead of using the actual iOS material, which is why they stopped looking like the rest of the system. It was not a performance decision and nothing was turned off on purpose: a build change made to fix an unrelated CI failure left the app asking for the native glass plugin by a name the web view cannot resolve, so the request failed every single time and the app quietly used its fallback. That fallback is meant to exist - it is what an older iPhone or the web app gets - so a broken plugin and an absent one looked identical from the outside, which is how this survived two releases. The build now ships the plugin properly, and a test reads the built app and fails if the bad name ever comes back, because this is not a bug you can see by looking at the screen.
Accessibility
- Asking for less transparency now turns the frosted surfaces off. The app had no handling for this at all: chips, the message bar and every sheet stayed see-through with a live blur behind them, which is precisely what those settings exist to prevent. They now switch to a solid surface, keeping their shape, border and shadow so a control over a plain white page is still visible as a control - which is what iOS itself does, measured on Apple's own round buttons. On iPhone and iPad the trigger is Increase Contrast rather than Reduce Transparency, because Safari does not report the latter to a web page; the controls drawn by the real iOS material already respond to both on their own.
- Settings' Logs and Activity pages had no margins at all. Both rendered flush against the divider that separates them from the section list, while every other page in Settings sat in a proper column - so clicking down the list made the text's left edge jump from a wide indent to nothing and back. They now use the same column as the other full-width pages in Settings (Usage, Mobile/Web), which also means the log console and the event list finally scroll on a narrow window instead of being cut off below the fold. Opening an event from Activity no longer shifts the text sideways either.
Changes
- Settings > Diagnostics > Activity is gone. It was a flat, reverse-chronological table of raw agent events -
tool_start,tool_end,thinking- behind a time-range picker, and stripped of which run had asked for them those rows were data rather than an answer. The same events are still recorded and still readable where they mean something: the Agents page groups them by session with each run's status, and opening an agent replays them as a transcript beside the messages that caused them. The endpoint that only this pane called (GET /api/agent-events, which it polled every 5 seconds while open, itself a contributor to the log flood below) is removed with it; the per-session endpoints are untouched. Diagnostics is now Logs and Advanced. - Settings' section list is ordered by what you actually open. Model & provider leads instead of General: every expired sign-in, API key and reasoning setting lives there, whereas appearance and language are set once and never revisited. General moves to the end of App, Usage leads Connect (it is a dashboard you read, not a setting you change), and Foxl Code comes before Foxl Notes.
- The three pages you only open when something is wrong now have their own group. Activity, Logs and Advanced moved out of App into a Diagnostics group at the bottom, so the everyday preferences are not padded by three pages you hope never to need.
- Settings' section list has no icons. Fifteen small glyphs down a narrow column competed with the words for the same glance, and two of them were the same glyph (General and Web Access both showed a globe), so they were not even a reliable way to tell rows apart. The list reads as a list of names now. The phone's Settings keeps its icons, where the rows are tall and the icon is what you aim at.
- "Chats" in Settings is now called "Trash." The pane restores chats you deleted, but "Chats" sitting in a settings list - in an app whose sidebar is already a list of chats - read as "your chats" rather than "the ones you deleted". Every language already used its own word for Trash in the notice that links here, so the two now agree.
- Settings on code.foxl.ai now only offers the settings that host can change. That site serves the Foxl Code product, but its Settings screen was the whole super-app's: the desktop agent's model and provider, Heartbeat, tool approval, deleted-chat restore, Integrations, Mobile/Web, Advanced, and the entire Foxl Notes screen were all listed, none of which configure anything reachable from there. The list was not even stable - it was the short version only while your desktop app was closed, so signing in from a machine running Foxl made nine more screens appear, on the same site, for the same account. Settings there is now Code, Account and General: the product, plus the two screens that genuinely apply everywhere (sign-in and plan; appearance, language and About). Nothing was removed from app.foxl.ai or the desktop and mobile apps, which is where the other products live. The three Foxl Code notification switches stay on Code's own screen, so they are still one click away.
- A settings link on code.foxl.ai now opens the screen it names.
/settings/accountthere opened the Code screen instead: the address was read correctly and then discarded, so no Settings screen on that host could be linked, bookmarked, or reloaded back to. The phone layout already got this right, so the same address meant two different things depending on the window size. - Opening a settings link on code.foxl.ai from a phone no longer puts the chat screen behind it. On a phone Settings slides up over the page you were on, and that page keeps showing through and is where closing it returns you. Opening a settings address directly - from a link or a bookmark, with no previous page to go back to - put Foxl Agent's chat home back there, which that site does not serve, so the sheet showed through onto the wrong product and closing it landed there. It now falls back to Tasks.
- The Settings button now says what it does. On the phone, Settings opens as a sheet with one round control in its header, and that control was a back arrow in both of its states - including at the top-level section list, where there is no previous screen to go back to and the only thing left to do is close the sheet. It is an X there now, and stays a chevron while you are drilled into a section, which is the one place it really does pop a level.
- That button is made of the same glass as the rest of the app. It carried its own private copy of the liquid-glass recipe rather than the shared one, so it was a thinner, flatter material than every other round chip - and it silently missed the tier that only phones get (a stronger frost, on the reasoning that the iOS web view renders it fully), plus the automatic de-frost while the keyboard or the drawer is animating. It now uses the shared recipe, so it matches the composer and header chips and picks up all of that by construction.
- "Usage today" on For You loads quickly, and says so while it loads. The card shows three numbers, and it was fetching them from the endpoint that builds the whole Overview dashboard - about fifteen SQLite aggregations plus process and OS telemetry, on a driver that blocks the server while it runs. Measured on a real 57 MB database, the work the card actually needed took 0.06 ms and the work it threw away took 19.2 ms. Two things were wrong underneath: the two heaviest queries group tool history by name, and the query planner chose to walk the whole table rather than seek the 24 hours asked for - which matters because those rows carry entire tool payloads, 7.5 KB on average and up to 2 MB each. Both now seek a covering index (340x faster, same results, with tie-breaking pinned so equal counts no longer reshuffle between polls), and the card reads a small endpoint of its own that returns 68 bytes instead of 758. Separately, whenever a refresh does take a moment, a small spinner now appears beside the card's title: the card used to display last-known numbers with no sign it was working, because the only progress indicator was gated on having nothing to show at all. It waits 400 ms before appearing, so a fast refresh no longer flickers.
- For You names itself, on every screen size. The page had no title of its own, so a status line ("All caught up") was the largest text on it, and on a phone the page name appeared nowhere at all - the shell header hides its copy on narrow screens for pages that print their own heading, and this was the one page in that list that never did. The title is now in the page, beside the sidebar toggle, with the status line reading as supporting text next to it.
- "All caught up" is capitalized like the heading it is. It read "all caught up" in English and five other locales.
- Quieter empty states in the For You inbox. The "No items yet" panel led with a decorative AI sparkle that repeated nothing the heading did not already say, and the empty Inbox and Archive views each drew the same icon as the tab the user had just clicked. All three are text-only now.
Features
- Foxl Code tells you when it is waiting on you. It could already say a pull request had opened and that one had merged, and nothing in between - so the states where the work is actually stuck on you were the ones that never reached your phone. A PR that is green and approved but not merged (the common case, since auto-merge is off by default), a merge attempt that was refused and now needs a person, a red build, and a reviewer asking for changes all send a notification now, each naming the repo and PR and opening straight onto the diff. Each thing notifies once, not once per webhook, and a merged PR stops asking - so this adds the notifications you were missing without adding noise.
- Foxl Code tells you when it files an issue. The autonomous loop could survey a repo overnight, decide on a dozen things worth doing, and file them - and the only surface that ever learned about it was a browser tab you had left open. Nothing was wrong with push delivery (a registered iPhone and a successful send were both on record); there was simply no sender for that event, so an issue arriving was an event with no notification attached to it. Now filing one pushes to your phone with the issue title and the one-line reason the loop picked it, so the decision is reviewable from a lock screen instead of only from a tab. It is a category of its own rather than part of "task results": that one fires when work you started finishes, this one when the loop chooses what to start, and folding them together would mean giving up merge notifications to quiet backlog chatter. Only the loop's own issues notify - an issue you asked for in chat is one you are already watching.
- Notification settings inside Foxl Code's own settings. The three Code categories - issues it files, questions it stops to ask, and task results - now have switches in Code's settings, next to the model and budget defaults they belong with, and they save the moment you flip them. They are the same preferences as the ones in the app's Notifications screen (one store, so the two can never disagree); this is just the place people look first. The card also states how many devices are registered for push, because with none registered every switch is correct and nothing can arrive - which is indistinguishable from a category being turned off, and is what made the missing notification hard to see in the first place.
- A long paste now stays where you put it in the sentence. Pasting something big into the composer turns it into an attachment card, and until now that card always went to the top of the message - so a prompt like "compare this log against this config" ended up as both pastes stacked above the sentence, with nothing saying which was which. The paste now leaves a marker at your cursor, right where you pasted (
[#1 server.log, 240 lines]), and you keep typing around it. When the message is sent, each paste is placed at its own marker, so the model reads it exactly where you meant it - and the sent message reads back the same way instead of front-loading every attachment. The card above the composer stays, numbered to match: click it to highlight its marker in your draft, click the marker and the card opens. Removing the card removes its marker, and if you delete a marker by hand the card says "not placed" rather than quietly looking the same as a positioned one - it is still sent, just without a position. Short pastes are untouched; they go straight into the text as always. - A pasted attachment could be silently dropped when sending from a phone. On mobile the composer only reports what you have typed when the field loses focus, and tapping send does not do that - so a message with both typed text and a pasted attachment took a code path that sent the raw text and never attached the paste. The attachment stayed visible in the composer the whole time, so there was nothing to notice: the model just never saw the pasted content. Both are now always sent together.
- The sidebar no longer casts a curtain of light under the conversation list. The chat and task lists in the sidebar are capped in height and fade at the bottom to show there is more below. That fade was painted on as a band of solid sidebar colour - but the sidebar is not solid: on the desktop app it is glass, and the wallpaper behind it supplies about a quarter of every pixel. So the band was painting the un-tinted colour over a surface the desktop had already darkened, and what came out was a bright wedge fanning up from the bottom edge, most obvious over a dark background. The fade now dims the rows themselves instead of covering them, so whatever is behind the list - glass, wallpaper, a solid panel - comes through untouched, in light mode and dark. It also measures the list rather than counting rows, so scrolling to the end clears the fade instead of leaving it to dissolve the last conversation you were looking at.
- A long paste now stays where you put it in the sentence. Pasting something big into the composer turns it into an attachment card, and until now that card always went to the top of the message - so a prompt like "compare this log against this config" ended up as both pastes stacked above the sentence, with nothing saying which was which. The paste now leaves a marker at your cursor, right where you pasted (
[#1 server.log, 240 lines]), and you keep typing around it. When the message is sent, each paste is placed at its own marker, so the model reads it exactly where you meant it - and the sent message reads back the same way instead of front-loading every attachment. The card above the composer stays, numbered to match: click it to highlight its marker in your draft, click the marker and the card opens. Removing the card removes its marker, and if you delete a marker by hand the card says "not placed" rather than quietly looking the same as a positioned one - it is still sent, just without a position. Short pastes are untouched; they go straight into the text as always.
Improvements
- The phone got its own shape. Corners on mobile came from two unrelated places:
--radius(8px) generated only the small/medium/large steps, whilerounded-xl/2xl/3xlwere Tailwind's own defaults, so half the scale could not move with the other half. One screen could show 6px, 8px, 12px, 16px and 28px corners at once, and the Settings sheet nested all five inside each other. There is now a single four-role scale - sheet, card, panel, chip - that steps up on a phone (28/20/14/10px) and stays exactly where it was on desktop (20/12/10/8px), so nested corners follow the rule that an inner radius is the outer one minus the gap between them. Desktop is pixel-identical. - Bottom sheets are one component again. Settings, the model picker, conversation search, notifications, the Notes AI panel and the Schedules filters were five different sheets: 28px all round, 28px on top only, 16px on top only, and - for notifications - an opaque square panel running edge to edge, with two grabber opacities and three ways to dismiss. They now share the inset, radius, frosted surface and grabber, and differ only in what they list.
- Dialogs are no longer square on a phone. The shared dialog carried
sm:rounded-lg, which does not apply below 640px, so the Notes onboarding, the EULA and Connect-desktop all rendered with hard square corners on iOS. Fixed for every dialog in all three products. - The chat home screen reads as one design. The suggestions under the greeting were hairline-divided rows with the smallest radius in the app, sitting between a heavily rounded composer and two circular chips. They are now cards in the composer's own shape, and message bubbles follow it too.
- Tasks is a phone screen, not a squeezed table. A five-column table on a 393px display gave a third of the width to the ID column and left two selects fighting over the rest. It is now a card per task, with sort controls that the phone previously had no access to at all (they lived in the table header).
- Everything you tap on a phone is now at least 44px, in all three products. Measured rather than eyeballed, and the results were worse than they looked: in the sidebar EVERY row was under the floor (nav rows 40px, the product switcher and the logo/Home button 24px, the account row 32px, New chat 28px), the chat composer's controls were 32-36px, and Notes' header controls were 28px. Buttons, inputs and selects now carry the floor in the SHARED components, so Agent, Code and Notes inherit it instead of each page fixing its own. Desktop keeps its denser sizes - a cursor is precise, a thumb is not.
- Typing in Foxl Code no longer zooms the whole app. Its composer was 14px, and below 16px iOS zooms the page when a field takes focus - so starting a task on a phone left you pinching back out to read the reply. The Agent composer has been 16px for this reason since the mobile pass; Code never got it. Its composer also ran under the iOS home indicator, where the send button was.
- Fewer rules, bigger targets. Pages built on the shared hero drew a divider and then their own, stacking three or four hairlines down a narrow screen, and the header repeated the page's own heading right above it. Both are gone on mobile. Header chips were five different sizes across five places (28/36/40/44px) - the agent view's back button was the smallest tap target in the app and its header was 12px shorter than the bar it replaces - and are now uniformly 44px. The Notes recording pill sat under the iOS home indicator; it now clears it.
Fixes
- A stopped server now restarts itself, and a failed restart says why. When the desktop app lost its local server it put up a panel with a Restart button and then waited - forever, if nobody was at the machine, which for a 24/7 agent is most of the time. Pressing that button often looked like it did nothing, and there were three separate reasons for it. The app was fighting itself: stopping the server made its own crash-watcher think the server had died, so the crash-watcher started one copy while the restart started another, and in a real log one press produced start, stop, start again - occasionally killing the healthy server it had just brought up. The button also could not tell you how it went, because it reported success the moment it had *scheduled* a start rather than when the server actually answered. And if the old server was somehow still holding the port, the new one quietly moved to a different port and reported itself healthy while the app kept talking to the old one, so the panel could never clear. All three are fixed: one restart owner instead of two racing ones, a result that means the server really is up, and a pinned port that fails loudly instead of relocating. The panel now retries by itself - three times, spaced further apart each time - tells you which attempt it is on, and if it still cannot recover it says what went wrong (the server never reported ready, exited on startup, or could not be launched) instead of re-enabling the button in silence.
- Foxl Code's message bar sits at the bottom of the chat again. Opening a task put the input bar directly underneath the message bubble, floating in the middle of the screen, instead of docked at the bottom the way Foxl Agent and every other chat in the product do. A task seeds its transcript with the prompt that started it, rendered as a real message bubble, and the "centered greeting" empty state was being applied to that bubble - so the two stacked in the middle of an empty canvas. Centering is now only for the new-chat greeting, and only on desktop, both of which is what Foxl Agent already did. The transcript also measures the composer now instead of assuming one line of text, so a long draft or a pasted block no longer covers the message you are replying to.
- The Work tab paints instantly on return instead of showing four skeletons. Issues and pull requests are cached per repo and refreshed behind the rows you are already looking at, so leaving the page and coming back no longer costs a full reload. The 20-second auto-refresh now pauses while the tab is in the background and catches up when you come back to it - a tab left open was refreshing 180 times an hour for nobody. Server-side, one request now mints one GitHub token instead of three, cutting six round-trips to four.
- Foxl Code looks for its own work by default. Foxl has been able to survey a repo it has worked in, open a GitHub issue for something worth doing, and start an agent on it - but the switch was off unless you found it in Settings, so for most people the loop simply went quiet whenever the queue emptied. It is on for new accounts now. It only ever surveys a repo you have already run work in, on a 6-hour cadence, stops while its own issues are unread, and stays out of anything risky (no dependency major versions, migrations, auth or billing changes). If you had already turned it off, it stays off. The composer also no longer promises to "draft an issue" when what it actually does is open a pull request.
- A notification that arrives on your phone now stays in the app. Every push the relay sends on its own - a task finishing, a question, a Notes transcript, and the new issue-filed one - arrived as a banner and left no trace: miss the banner, open Foxl, and the Notification Center said "Nothing here". The only thing writing to it on the phone was a frame relayed from your desktop, so it needed your Mac awake to record anything at all. Pushes that arrive while the app is open are now filed like any other notification, with the same Open button pointing where the banner would have taken you. (A push that arrives while Foxl is closed is still the OS's to hold - tapping it opens the right screen, as before.)
- Foxl Code's settings no longer collapse into one line of text when its server is unreachable. A response that arrived without a body - which is what a missed route looks like, since both hosts answer unmatched paths with the app shell and a 200 - was read as if the settings were present, and the resulting error took the whole pane down: no cards, no switches, no save button, just a sentence. It now says what happened and keeps the notification switches usable, since those come from a different service and have nothing to do with whether Code's server answered.
- The save bar at the bottom of Code's settings is no longer a floating strip. It was pinned to the bottom of the window with a full-bleed border sized for one specific layout, so on the other two it stopped short at both ends and read as something pasted over the page - and on iOS it added a second home-indicator inset on top of the one the settings sheet already applies, leaving a dead band under the button. The action now sits in its own panel and scrolls with the rest of the page, the way every other settings control in the app does. It is also disabled until something actually changes, instead of permanently offering to save a page nobody had touched.
Features
- Notifications you can actually read on your phone. Every notification the app raised on iOS or the web went into the store and bumped the home-screen badge, and then had nowhere to be read: the notification bell only ever existed in the macOS titlebar and the Windows header, and the phone has neither. The badge could say 12 with no way to see what the 12 were. There is now a Notification Center on those builds, opened from the account row at the bottom of the sidebar - which carries the unread count, so the badge has somewhere to lead. Rows swipe the way they do in Mail: left to delete, right to mark read, and right again to mark it back to unread, because a one-way gesture turns "keep this for later" into a trap. Inbox and Archive are both there (archiving has existed in the app for months with no button on the phone to reach it), items group under Today / Yesterday / Earlier, and the gear in the corner goes straight to your push settings. The desktop bell is unchanged.
- code.foxl.ai is now the same app as app.foxl.ai, so signing in with a password works there too. Foxl Code had its own separate front-end. Only the page bodies were shared with the main app; everything around them - the sidebar, the chat surface, the header, the sign-in screen - was a second copy, and the two drifted apart in small ways that were hard to name but easy to feel: nav items in a different order, a chat that looked subtly unlike the one in the app, and a sign-in page that only ever offered GitHub, Google, Apple, and a magic link. Setting a password in Settings did nothing for code.foxl.ai, because that page had no field to type it into. The second front-end is gone. code.foxl.ai now serves the app itself, opened directly into Foxl Code, so the password option (and anything else that lands on the sign-in page in future) is simply there. Everything you had keeps working: the same links (
/tasks,/repos, and links to individual tasks), the same GitHub connection, the same sign-in session, and older links like/filesand/activitystill resolve. The ONLY difference from app.foxl.ai is that the Agent/Notes product switcher is not in the sidebar, since this host serves one product; every other screen is the same code. - Staging pages now say they are staging. The yellow "this is not production" bar existed on stg.code.foxl.ai but never on stg.app.foxl.ai, which was indistinguishable from the real thing. Both show it now, naming the host you are actually on.
- Foxl Code has a Work tab: every open issue and pull request in one place, with the two buttons that close the loop. Foxl Code could run agents and open pull requests, but the work going IN (an open issue) and the work coming OUT (a pull request waiting on you) were only visible on github.com - so the story that begins "your agent works while you sleep" ended with you opening a browser at both ends, and the state that actually matters ("three of these are green and waiting on me, two issues have nobody on them") was not shown anywhere. Work lists the repo's open issues, its open pull requests, and what recently merged, each row joined with what Foxl knows about it: which agent is on it, whether the build passed, whether the reviewer approved. Open issues is the tab you land on, because that is the work that still needs a person. On any issue nobody has picked up, Solve puts an agent on it in one press - the issue text is re-read from GitHub at that moment rather than trusted from the page, the agent gets its own branch, and a comment goes on the issue so anyone reading it on github.com can see work started. An issue that already has an agent shows that agent instead of a second Solve button, so one issue never gets two competing pull requests.
- You can now see what changed and merge it, from the phone, without opening GitHub. Every pull request row has a diff view - per file, per line, additions and deletions - and a Merge button. A merge notification used to be a dead end: it said "PR merged" with no indication of what moved, and tapping it did nothing but bring the app to the front on whatever screen you left it. Notifications now carry the shape of the change ("foxl-ai/pilot #41 - +42 -8 across 3 files"), which is often the whole answer, and tapping one opens that pull request's diff with Merge next to it. None of that costs an extra request: the numbers were already in the GitHub event Foxl receives and were simply being discarded.
- Auto-merge, off by default and on your terms. The default is unchanged and is the recommendation: you read the pull request and press Merge. Turn auto-merge on (Settings -> Auto-merge) and a pull request lands by itself, but only when BOTH gates pass - the build reports green AND the reviewer agent approved it. Neither one alone merges anything, and your own branch protection still applies on top. Both gates are checked against the exact commit that would be merged, not just as a yes/no: a build that went green on an earlier commit does not count for a later one, so a push landing mid-decision cannot slip in ungraded - the pull request simply says the green build was for an older commit and waits for the new one. A pull request whose required checks have not all reported is treated as not ready rather than merged-and-refused. When GitHub does refuse a merge (a conflict, a check Foxl cannot see) that is recorded and shown on the row rather than retried in a loop, and every pull request that looks green but has not merged says which gate it is still waiting on - so the setting can never look silently broken. Merges are logged with whether a person or the loop decided, because GitHub reports the same bot account for both and only that record can tell you afterwards. In a shared organisation install, any member can retry a merge that was refused and every member sees the same reason it was refused.
- Approve a pull request and an adversarial reviewer decides whether it merges - yours, a teammate's, or one Foxl never touched. Approving is the cheapest thing you can do on GitHub and reading a diff properly is one of the most expensive, so this splits them: you press Approve to say "I want this in", and a reviewer that has actually read the change decides whether it goes in, comments either way, and merges when it agrees. Two things had to be built for that, because neither existed. Nothing in Foxl was listening for an approval at all - the GitHub event that fires when you submit a review reached no code in the product, so pressing Approve on github.com did precisely nothing here. And the reviewer could only ever see Foxl's own work: it gave up immediately on any branch its agents had not created, then required a matching Foxl task, so a pull request a person pushed by hand failed both tests and there was nowhere to even record that it had been reviewed. Both are fixed, which is what makes this work on a repo where people and agents commit side by side. Your approval is treated as a request, not as the verdict - the reviewer is told, in as many words, that an existing approval is not evidence the code is correct and that it must not defer to one, because a reviewer that agreed with every approval would be theatre and here a passing verdict can merge. If it finds the change does not do what the pull request claims, it says so and refuses to merge. A verdict has to name the commit it read, and the merge is pinned to that commit: pushing while a review runs blocks the merge instead of landing a commit nobody graded, and a verdict that names no commit at all is refused rather than trusted, because a missing answer is not a passing one. A draft, a conflict, a required check that has not reported yet, or a mergeability answer GitHub has not finished computing each stop it with a comment saying which - "no conflicts" and "the checks passed" are different questions and both get asked. Nothing here can block a merge you could otherwise do by hand: every refusal leaves the pull request open and yours, and your branch protection still applies on top of all of it. Off by default in two separate switches (Settings -> Review on approval): reviewing on approval spends a model call whenever anyone approves, and merging on the reviewer's verdict writes to your default branch, and wanting the first without the second is a reasonable place to stop.
- Foxl Code can now find its own work instead of going quiet when yours runs out. The autonomous loop only ever advanced work you had already queued, and the way it was woken made that a hard limit rather than a design choice: it looked for users with a task in flight or a plan in progress, so the moment the last agent landed and the last plan completed, it stopped being woken at all. Not idle - gone. It could not notice a build that went red an hour later, a dependency drifting behind, or an unfinished follow-up from its own pull request, because nothing was running to notice with. The tool for filing an issue had been there since the first release and no unattended run had ever called it; every issue in the repo came from someone asking in chat. There is now a third way to be woken: with your queue empty, the loop surveys the repo on a slower schedule of its own, and when it finds something worth doing it opens a GitHub issue explaining what and why, then starts an agent for it. An issue is the unit deliberately - it is reviewable, closable, and visible to someone who never opens the app, which silent work is not. It is off until you turn it on, under Settings -> Autonomous loop, because unlike the rest of the loop this one chooses work rather than doing what you asked. It stops surveying while it already has its own issues open, so closing them is what invites it to look again; it stays out of anything risky (no dependency major versions, migrations, auth or billing changes, no deleting features - it can describe those in an issue but will not start them); and finding nothing worth doing is a normal outcome that files nothing, since a made-up task is the one result that would make this worse than silence.
- The window glass now settles back when Foxl is not the app you are using. A real material goes quiet when its window is not in front, and that behaviour is a large part of what makes the window read as a pane of glass rather than a panel with a tint on it. It was not happening: nothing in the app was watching the window's focus, and the two places that could have done it for free are both dead ends - at the tint that keeps the sidebar legible the material only owns about a quarter of the pixel, so macOS dimming what it owns is barely perceptible, and on macOS older than 26 the glass fallback pins itself active, which switches the system behaviour off outright. Switching away now drains the key hue out of the canvas, sidebar and titlebar over the same 420ms the app already uses when it finishes launching, and switching back restores it. The transparency itself does not change, which is deliberate: that is what holds the measured contrast floor for small sidebar labels over a dark wallpaper, so the colour moves and the alpha does not - which is also what the system does, since an inactive macOS window goes flat, not dark. The content card is untouched.
- Your chat history stays readable when the desktop is offline. On the web app and the phone, the conversation list and every transcript came from your desktop over the relay, so the moment it went to sleep the whole history disappeared: an empty sidebar and a chat that would not open. Conversations you have opened are now kept on the device, encrypted, and are what you see in that grey state - the list, the full transcript, search and copy all work. The saved copy is read-only, and says so in place of the message box: replying needs the desktop, because it is the desktop that holds your files and tools, and answering without them would be a different assistant pretending to be the same one. Nothing extra is stored on our servers - this is a copy of your own data, on your own device.
- What is kept on the device is kept per person, and encrypted. Only conversations you actually opened are saved (there is no background download of everything), and attachments are never copied - the text is kept and the file is named. Deleting or archiving a chat, or renaming it, reaches the saved copy too, so a chat you threw away does not stay readable offline.
- Foxl Code can stop and ask you something, and you can answer from a locked phone. The loop had only one way to raise a question that survived being unread: a plan parked for confirmation, which is the rarest path it takes. Everything it actually needs to ask about day to day - two repos match the name you gave, this step will force-push, the change is bigger than one pull request - came out as a sentence in a reply, and a sentence in a reply is seen by an open tab and by nothing else. Phone locked and laptop shut, the run simply stopped, nobody was told, and the loop stopped looking at you at all, so it could not even wait properly. Questions are now kept as real, durable items: they reach you as a notification you can type the answer straight into, without unlocking anything or opening the app, and a matching banner on the desktop. The loop picks the answer up on its next pass and carries on from where it stopped. If nothing can reach you the question waits in the app instead of being lost, and if it goes unanswered for a week it lapses rather than waiting forever - lapsing is never read as "you approved". These notifications are opt-out under Settings -> Notifications, next to the other Foxl Code ones.
Changes
- Activity and Logs moved into Settings; For You is now just the feed. The For You page had three tabs across the top - For You, Activities, Logs - all sharing one URL. Activity (the agent event log) and Logs (the server console) are things you read when something looks wrong, not every day, so they now live in Settings next to the rest of what you inspect, each with its own row and its own deep link. For You keeps its place in the sidebar and drops the tab bar, so the sidebar entry and the page are finally the same thing. Nothing was removed: both views are the same code they always were, and the command palette finds them by name ("logs", "events", "agent log") instead of dropping you on Advanced.
Fixes
- A file path an agent was given could point outside its own workspace, and one tenant's agent could write into another tenant's files. Every file an agent reads or writes goes through one containment check, and that check trusted a shortcut: it resolved the part of the path that already existed and then re-attached the rest by arithmetic. A symbolic link - a pointer from one path to another - broke the arithmetic, and a link pointing at a file that does not exist YET broke it in the worst direction, reporting a path outside the workspace as inside it. Anything then written landed wherever the link pointed. This did not need a tool call to set up: a link committed in a repository arrives with an ordinary
git clone, so simply asking an agent to work on a repo was enough to plant one. Reproduced on Linux at the real layout: six of eight planted links produced a write outside the workspace, two of them into another user's SSH keys and npm credentials. The check no longer takes shortcuts - it walks the path one name at a time from the workspace root, resolves every link it meets before deciding anything, and refuses on any error rather than guessing. Ordinary symbolic links inside a workspace keep working, because repositories legitimately contain them. Also fixed: the test that was supposed to catch this passed on macOS and failed on Linux for reasons peculiar to macOS temp directories, and it had never run in CI at all - it now runs on Linux on every pull request. - Foxl Code's request for a decision could never reach you. The agent gained the ability to stop and ask a question in the previous change, and the server-side list of calls a running agent is allowed to make was tightened in this one - but the new question call was not on the list, so every attempt would have been refused. Nothing would have been shown: the agent receives a plain error and carries on, so a question would simply never arrive. It is on the list now, for asking only - answering stays with you.
- On staging, pressing Connect sent you to the production site. The GitHub connection flow worked out which address to return to from a setting that is only present on one of the two paths through it; on the other path it fell back to the production address, spelled out in the code. From staging that meant landing on the real site, which could not make sense of a staging request, with no way back. It now reads the address of whichever site you are actually on.
- On the phone and the web app, the Notification Center was always empty. Every notification the desktop raises - a schedule finishing, an agent completing or failing, a heartbeat result, a feed item - reached the desktop and stopped there. The relay knew how to pass those on and the phone knew how to display them, but the desktop never put them on the wire: the part of it that feeds the relay forwarded only terminal events, so nothing was ever sent. The result was a notifications screen that could only ever say "Nothing here", and a home-screen badge with nothing behind it, on exactly the devices that have no other place to read a notification. The desktop now forwards them, so they arrive on app.foxl.ai and on iPhone as they happen, land in the Notification Center to be read later, and count toward the badge. This is also what finally makes the "Schedules" push toggle do something - a 6am job can now reach a phone in a pocket instead of notifying a Mac nobody is sitting at. Muting a type in Settings still silences its banner and still keeps the entry readable, exactly as on the desktop, and the desktop's own notifications are unchanged.
- Foxl Code pages other than Tasks no longer fail to open. On the old code.foxl.ai, /runtimes threw
useTaskSelection must be used inside a TaskSelectionProviderand rendered nothing - the old front-end supplied that provider only around its Tasks routes, while Runtimes (and the task plan panel and command palette) all need it. Serving the main app fixes it structurally: there, one provider wraps every Code page. - Staging talked to the production relay. Every automatic staging deploy built the app without telling it which relay to use, and the only rule it had left was "this is a production build, so use the production relay" - which is true of a staging build too. So the staging versions of both the app and Foxl Code were signing in against real accounts and spending real credits, when the entire point of staging is that it cannot touch any of that. Staging pages now recognise themselves by their own address and use the staging relay, so this holds however the site is deployed rather than only when someone remembers to pass the setting. The desktop and iOS apps are unaffected and still use production.
- A failed GitHub connection said nothing at all. After connecting the GitHub App, GitHub sends you back to Foxl Code with the outcome attached to the address - either "connected" or the specific reason it did not work (you are not a member of that organization, the request expired, and so on). Foxl Code was dropping that part of the address before any page could read it, so a connection that failed looked exactly like one that had never been attempted: no message, no reason, nothing to act on. The result is read again, so a success confirms itself and a failure tells you what went wrong. The address is tidied afterwards, so reloading the page does not show a stale message.
- The Work tab opened Tasks instead. Foxl Code's short web addresses are spelled out one per page, and Work - added at the same time - had no entry, so code.foxl.ai/work quietly fell back to the Tasks page. Links to Work, including the one in a merge notification, land on Work. Adding a Foxl Code page without its address is now a build error rather than something that only shows up when someone follows the link.
- Foxl Code no longer shows an error screen when it cannot reach its server. Tasks, Repos, and the task plan panel each read a list from the server and used it without checking one had arrived, so a reply that carried no list took the whole page down with "Something went wrong" - two error boxes at once, in the sidebar and the main pane. It reads as a broken product when the honest answer is "nothing to show yet". Those screens now render their normal empty state.
- The web app could only be built on one particular machine. It depends on the iOS-only glass plugin, which lives outside this project and is found by counting directories upwards - so it resolved on the laptop it was written on and nowhere else, failing the build outright anywhere the folder layout differed, including on the build servers. The plugin is now declared as something supplied at runtime rather than compiled in, which is what the code already assumed: both places that use it load it on demand behind an "are we on a real device" check and fall back to the CSS glass when it is not there. On the web that fallback is the intended path, and on iOS the real plugin is still loaded. A second cause of the same symptom is fixed too: two shared internal libraries listed none of the packages they actually use, and had been getting away with it because whatever happened to be installed nearby satisfied them. On a clean install nothing did, and the build stopped on a missing package. Both now declare what they need.
- Refreshing mid-reply showed a dead "..." and could prefix the answer with it. While a reply was being generated the app saved a placeholder "..." into the chat as the assistant's actual text, because the draft is created as soon as the model starts thinking or uses a tool, before it has written anything. Reloading the page then read that placeholder back as a real answer: the animated typing indicator was replaced by three dots that never moved, and when the reply resumed it was appended to them, so the finished answer began "...". The draft now starts empty, and chats already saved with the placeholder are corrected when they load.
- A refresh could leave a reply that was still being written looking finished. Reattaching to a reply in progress happens over a single request made as the page loads, and if that request failed - which it does when the app is reloaded faster than its local server can answer - nothing tried again. The reply kept being written but the window never showed another word of it until it was reloaded by hand. It now retries while the server comes up.
- A connection that died without saying so was never noticed. After the machine slept, or wifi dropped, both ends could be left holding a connection that looks open and carries nothing. The app went on believing it was connected and stayed silent. Connections are now checked, so a dead one is closed and reconnected.
- The app stopped trying to reconnect in several situations. Reconnection was attempted only when a connection closed cleanly, at a fixed two-second interval that never gave up, and a page navigation could leave old attempts running that fought with the new ones. It now backs off when the server is genuinely down, reconnects immediately when the app is brought back to the foreground or the network returns, and no longer leaves stale attempts behind.
- Conversation search sorted by date, so the result you meant was rarely at the top. Searching ordered matches by last activity, which meant a conversation actually titled what you typed lost to any chat that merely mentioned the word more recently. Results are now ranked by how well they match - a title match first, then how many of your words were found, then how often - with recency only deciding between comparable matches. Ranking happens before results are paginated, so scrolling for more keeps the order intact.
- Search results now show when each conversation was last active. The desktop titlebar dropdown showed no date at all. Both search surfaces now show one at the right edge, which matters more than before: results are ordered by relevance, so position no longer tells you how recent a match is.
- On the web and mobile app, search ignored conversation titles entirely. It only looked inside messages, so a conversation whose title was exactly what you typed did not appear, and the "Titles" filter was permanently empty there. Multi-word queries also only matched when the words were adjacent, while the desktop app found them apart. Both now behave the same way in every shell.
- The match count beside a search result counted words, not messages. A single line containing "relay timeout" showed "2" - one per word - implying two places to look when there was one. It now counts the messages the query actually hit.
- Retrying a coding task carried the failed attempt's changes into the new pull request. A retry deliberately reuses the original run's machine so the checked-out code and installed packages do not have to be fetched again, but nothing reset the code back to the branch being worked from. The retry therefore began on top of the attempt that had just failed, and the pull request it opened contained that attempt's commits as well as its own. A retry now starts from the current state of the target branch, which also means it picks up anything merged in the meantime, while still reusing the installed packages that made reuse worthwhile. All four coding backends were affected.
- Signing out now clears your data from the browser, not just your tokens. Sign-out had five separate implementations across Foxl, Notes and Code and none of them shared a cleanup step, so on a shared computer the next person to sign in inherited the previous person's chat history, notification text, meeting recordings, saved workspace paths, profile photo, the tools they had permanently approved for the agent, and their own Anthropic API key - which was still usable. Every sign-out route now runs one teardown, and signing in as a different person clears what the previous one left behind before anything is shown. Device settings you would not expect to lose - theme, language, panel sizes - are deliberately kept.
- Regenerate and Edit no longer appear on a transcript you cannot reply to. They were still offered on a deleted chat and on an offline saved copy, where pressing them either failed or would have quietly continued the conversation somewhere it did not belong.
- Deleting or renaming a chat while the desktop is asleep no longer looks like it worked. The web app was answering its own delete, rename and archive requests as successes when there was nothing to send them to, so a row would leave the sidebar or a title would change and then come back on the next sync - and "Delete all chats" in that state cleared the copies saved on your device while leaving every chat on the desktop untouched. Those actions now say the desktop is needed instead of pretending.
Improvements
- The typing dots actually move now. They previously only dimmed in place, which read as a flicker rather than as progress. They now rise and swell in sequence as a single wave. The travel is removed for anyone who has asked their system to reduce motion, and the dots stay visible.
- Foxl Code starts coding the moment you ask. Asking for a code change used to get you a question back - "I'll start an agent in your repo. OK?" - and only after you answered did anything begin. Every task cost a round-trip before the first agent existed. Now a coding request turns into running agents in the same reply: Foxl tells you what it started instead of asking permission, and several agents fan out at once when the work splits into separate pull requests. It only stops to ask when it genuinely cannot proceed - no repo selected, a target that does not exist, or work that is hard to undo. Details it can settle by reading your repo (which file, your naming conventions, how your tests run) are the agent's job to find, not a reason to interrupt you.
- Wide work still shows its cost first. A request that splits into more pull requests than your per-turn allowance (three by default) publishes a plan with an estimated cost and one approval button, so a large refactor never starts spending before you have seen the number. Both the switch and the allowance live in Settings under "Instant spawn"; turning it off restores approving every task.
- A starting agent looks like one. A spawning agent used to render as a collapsed grey line labelled with an internal tool name, and its result as raw JSON. It now shows the task it is working on, opens as soon as it appears, and every agent in a fan-out lands in your task list immediately instead of waiting on the next refresh.
Fixes
- A billing hole let frontier models be charged at the cheapest model's rate. Model names are normalised before use, so a request could name a model with a harmless-looking suffix, pass the plan check on the real model, be served that model, and then be priced by a lookup that fell back to matching any cheaper name inside the string. Measured against the live price table, Fable 5 output could bill at 1/83rd of its rate. Billing now always uses the model the request actually resolved to, the guessing fallback in the price lookup is gone, and a model with no price row bills at the highest rate rather than a mid-range one.
- Windows updates were installed without checking who signed them. The updater does verify signatures, but only when it is told which publisher to expect - and it was not, so the check silently passed on every Windows update. Now configured, so an installer that is not ours is refused. macOS was never affected: the operating system verifies our notarized signature there regardless.
- A page you visited could read the desktop app's local access key. The app's local server checked which sites may READ its responses, which browsers enforce, but never checked which address the request claimed to be for. A site pointing its own domain at your machine therefore looked same-origin to the browser and could fetch the page that carries the key - and with it the whole local API. The server now refuses requests that do not arrive as localhost.
- A coding-agent task could reach every repository the Foxl app was installed on. Each task was given a GitHub token covering the whole installation, and that token sits in the environment of a shell running with permissions checks disabled, on a repository whose contents are not ours. Anything that could influence the agent could therefore have pushed to any of those repositories. Each task now gets a token for its own repository only; GitHub refuses everything else at its end. Verified against live GitHub: with the scope, write access to the other repositories returns 403 and 404.
- The credential given to a coding-agent task could act as your whole account. Foxl Code hands each task a short-lived token so it can call the model. That token was also accepted by every other signed-in endpoint, so anything that could influence the agent - a crafted prompt, or content in the repository it was working on - could have read the account, changed its password, unpaired the desktop, or deleted the account outright. The token is now restricted to the model endpoints and refused everywhere else. Verified against a running relay in both directions.
- Any web page you visited could take control of your browser through Foxl. The desktop app listens on a local port for the Foxl Chrome extension, and that one path accepted connections without any check. WebSocket connections are not covered by the browser's usual cross-site protections, so a page in another tab could connect and switch on the tool that drives your real, logged-in Chrome - navigating, clicking and typing as you. The path now only accepts connections that genuinely come from a browser extension, which a web page cannot pretend to be. Verified against a running server: a page origin is now refused, the extension still connects.
- The incident kill switch did not stop two of three background jobs. Turning the service off stopped every interactive request but left the per-minute job that mints GitHub tokens running, along with the one that can cancel in-flight tasks. The switch is now checked once where the jobs are launched, so it covers all of them, including any added later.
- A Google OAuth client secret had been committed to the repository since May and was still accepted by Google when checked. Nothing used it. Removed, with the file pattern blocked so it cannot return. (Rotating it is a separate console action.)
- On iPhone, the "answering" spinner sat on top of the chat title in the sidebar. The spinner overlaps by design and is normally moved out of the way by hovering, which touch cannot do. The title now leaves room for it, and only while a reply is actually arriving.
Features
- Read a deleted chat before deciding to restore it. Clicking a chat in the deleted list now opens it in the normal chat view, rendered by the same components as any other conversation, with the composer replaced by a line saying the chat is deleted and where to restore it. Reading does not restore: browsing four deleted chats to find the right one leaves all four deleted. The list itself is a card with its own scroll and paging, so a long list can no longer push the controls below it off the screen.
- Restore a chat you deleted. Deleting a chat has always kept it for seven days before removing it for good, but nothing in the app could see into that window, so a chat deleted by mistake was gone as far as you could tell. Settings now has a Chats section listing what you deleted, how many days each has left, and a Restore button. You can also delete one permanently right away, or empty the whole list, instead of waiting the week out. The recovery endpoints had in fact been written some time ago and never worked: the listing was registered after the route that fetches a single chat by id, so the web server read the word "trash" as a chat id and always answered "Conversation not found". The feature was unreachable rather than missing. One thing restore does not bring back, and the panel says so plainly: the entries your chat left in the memory log. Those are removed the moment you delete, which is the point of the next item.
Fixes
- Deleting a chat now also removes what you said from the memory log, immediately. Alongside the database, Foxl keeps a day-by-day memory file containing your actual turns - your words and the reply, verbatim - and reads it back as context on later turns. Nothing connected those entries to the chat they came from, so deleting a chat could never remove them: they stayed on disk and kept being read back. Each entry now records which conversation it belongs to, and deleting a chat strips its entries out at once rather than at the end of the seven-day window, because the memory log is not part of what restore brings back and there is no reason to keep it waiting. Entries written before this change carry no such marker, so they cannot be attributed to a chat and are left alone. What you deliberately asked Foxl to remember - its identity, your notes, your topics - is never touched by any of this.
- The deleted-chat notice ran past the transcript on both sides, because it had no width of its own inside a bar that spans the window. It now sits in the same column as the message box, narrower and quieter, and "Settings > Chats" in it is a link that opens that pane instead of an instruction to go find it. Checked at desktop, iPhone 17 Pro and iPhone SE widths.
- A deleted chat appeared in the sidebar and stayed there. Opening a deleted chat to read it added it to the sidebar's chat list, so it looked un-deleted: the row survived clicking through to other conversations and only disappeared after a reload. Reading a chat from the trash no longer touches the sidebar. Clicking a different chat also left
?deleted=1on the address bar, which would have sent a later reload of that URL down the trash path for a live conversation; the address now updates properly. - The Foxl logo had a brown eye on two screens. The mark is duplicated as inline SVG in two places that cannot load an asset file - the page shown after signing in with Google, Apple or GitHub on desktop and mobile, and the screen Foxl shows if it fails to start. Both had the eye recoloured from blue to brown in v0.5.1 and it shipped in every release since. Restored to the canonical blue, taken from the logo file itself rather than retyped, and a test now compares every inline copy against that one source.
- "Signed in. AWS credentials are ready." was shown even when they were refused. Signing in with AWS SSO proves who you are; it does not grant access to an account. The two come apart routinely, and when they did the app showed a green success message next to its own "No access" error in the same panel, then failed the next chat with a permission error that looked unrelated. The success message now appears only when credentials actually work, and when they do not you get the real reason.
- Being told to request access you already have. When an SSO sign-in turns out to grant no accounts at all, Foxl now checks whether another profile on your machine works and names it, instead of only advising you to ask an administrator. On the machine where this was reported two profiles for the very same AWS account were working the whole time.
- The AWS settings panel took 13 seconds to load. Diagnosing the sign-in problem above meant asking AWS what your credentials can reach, and the first version did that one profile at a time, on every refresh of the panel. Those checks now run together and their results are cached, bringing the panel back to well under a second.
- Deleting a chat did not delete what you said in it. Nine tables hold pieces of a conversation and only one of them was set up to clean itself out, so removing a chat removed the chat and left the rest on disk - including a table that stores the text of the instructions you gave, word for word. On this machine that was 342 leftover rows from chats that had been deleted, some months ago, still holding their prompts. That is the opposite of what deletion should mean and of what the privacy policy says. Deletion now clears every table that belongs to the conversation, both delete paths go through the same code so they cannot disagree about what "deleted" means, and rows left behind by earlier deletes are swept up automatically. Memory needed care rather than a blanket wipe. The memory store holds two different kinds of thing: what you asked the agent to remember across sessions - its name, your project notes, your channel settings - and leftover run-state entries from a feature that no longer exists, whose entry NAME was your prompt. Only the second kind is removed. Your actual memories are untouched.
- Chat errors were being replaced with a guess, and the guess sent people to the billing page for problems money cannot fix. Any failure whose text contained "403" was rewritten as "This model is not included in your current plan. Upgrade in Account." A Pro user signed in to their own AWS account hit that, and on that path a 403 is almost never about a Foxl plan - it is an expired sign-in, a role without permission to call the model, or a model not enabled in that region. All three are fixable in a minute by the person reading the message, and all three were replaced with an upsell. Errors now keep the original text and add a hint above it, so you can read, search, or paste what actually happened. AWS failures get AWS advice.
- The app said "SSO session expired" while the session was valid for another fifty minutes. The check that looks for a live AWS sign-in was written with a call that does not exist in this part of the codebase, so it failed every single time - and because the failure was caught and treated as "no session found", the app confidently reported the opposite of the truth. You signed in, the browser confirmed it, and Foxl told you that you had not. Two more places had the same mistake: the system report crashed for anyone with an audit log, and a tool-caching helper would have crashed the first time it was used. A check now runs on every build so this class cannot return.
- The app now tells you which AWS accounts you can actually use, instead of letting you pick one that was never going to work. A profile in your AWS config is a request, not a permission: it names an account and a role, and the account grants that separately. Foxl was asking you to choose a profile and finding out the hard way, several layers later, that the choice was refused - and then blaming your Foxl plan for it. It now asks AWS the same question the AWS CLI asks: which accounts and roles are assigned to you. Profiles you cannot use are marked, and if the answer is none at all, it says so plainly rather than leaving you to guess why everything fails. That last case is what actually happened: a valid sign-in with zero account assignments.
- A profile that had been deleted from your AWS config was still being used. If you picked a profile months ago and later replaced your AWS setup, Foxl kept using the old name - which resolves to nothing, so it also ignored credentials that WERE working. It now notices the profile is gone, says so, and falls back to your default credentials instead of dead-ending.
- "Ready" no longer means "we found a token somewhere". Signing in to AWS proves who you are; it does not prove the account has granted you the role. Foxl was reporting ready in that case and letting every request fail downstream. It now tries to actually obtain credentials and, when the account refuses, says that the role is not assigned to you rather than blaming your plan.
Fixes
- On the phone, swiping a chat to rename or delete it kept closing the sidebar instead. Two leftward swipes shared the same screen: one closes the sidebar, one reveals Rename and Delete on a chat row. The closing gesture listened everywhere and triggered after a shorter drag than a row needs, so it won every time and the row actions were effectively unreachable. Closing now only works when the swipe starts on the covered content next to the sidebar, which is where you would reach anyway to push it back. Swipes that begin on the sidebar itself belong to the sidebar.
- Bring your own Bedrock key was impossible to set up, so users kept paying us instead of AWS. The "SSO Login" button in Settings had never worked: it posted to an address on the local server that was never built, so the request failed, the code checked a field that was not there, and the error was thrown away. The spinner flashed and nothing happened - no message, nothing to act on. A user reported it as "when i press SSO login nothing happens" and then, reasonably, "i think as of right now im just wasting ur money because im going through foxl". They were right, and a broken bring-your-own-key path bills us for inference someone was trying to pay for themselves. The sign-in now actually runs, opens your browser, waits for you to finish, and tells you what went wrong when it does not - a missing AWS CLI, a profile that is not an SSO profile, or a sign-in you did not complete in time. The same screen was also lying to them. It showed "Ready" beside AWS Bedrock whenever an SSO profile merely existed in the AWS config file, which says nothing about whether you are signed in: the config entry is written once, while the actual credential is a token that expires every few hours. On this machine that meant 25 cached files, of which 14 were not credentials at all and 11 were expired, all reading as "Ready". It now checks for a live session and, when there is not one, says the session expired and points at the button that fixes it.
- The privacy policy said we never share your content with model providers. That was not true, and it was our most serious inaccuracy. The page I published this morning stated plainly that conversations, notes and code "are not used to train Foxl or any provider model". Checking that against the live AWS account rather than against memory: the account is configured for provider data sharing, in both regions, since June. It was enabled because one of the models we offer requires it - but the setting applies to the whole account, not to that one model, so it covers every request to a hosted model regardless of which you pick. The page now says what is actually the case: Foxl trains nothing, and content sent to a hosted model may be shared with that model's provider under their terms. Anyone who would rather it were not can use their own provider key, which bypasses our account entirely - so the policy now gives you a way to act on the information instead of a promise we could not keep.
- Stored passwords were six times weaker than they should have been, because of a comment I wrote that was simply untrue. The code said the platform capped password hashing at 100,000 rounds and that nothing stronger was available, so that is what shipped. The cap is per call, not per password: chaining six calls reaches the 600,000 the current security guidance asks for, and costs 68 milliseconds. Every new password now uses the full strength, and any password set before this is upgraded automatically the next time you sign in, with no interruption. This only matters if our database were ever stolen, which is exactly the scenario the number exists for.
- The record of failed sign-in attempts could be turned back into a list of email addresses. Attempts are counted against a scrambled form of the address rather than the account, so that guessing at an address that has no account behaves identically to guessing at one that does - otherwise eight attempts would reveal who has an account. The scrambling used a plain hash, which for something as predictable as an email address is barely scrambling at all: forty-nine guesses recovered a real address in a millisecond. It is now scrambled with a server-side key, so the record cannot be read back by anyone who does not already hold that key.
- The same password could be rejected depending on which keyboard typed it. Accented characters have two valid encodings, and the two produce different hashes. Someone could set a password on one device and be unable to sign in on another, with no explanation available to them or to us. Both are now treated as the same password.
- Deleting your account left your email preferences behind. Two tables that key on your account - your email subscription settings and the record of marketing mail sent to you - were not in the deletion list. They are now. The unsubscribe suppression list is deliberately kept, so a deleted and recreated address does not start receiving mail it had opted out of.
- A free user could pick a model their plan cannot run, and be told nothing. Locked models were greyed out and genuinely unclickable, so tapping Opus 5 did precisely nothing - no explanation, no way forward, at the exact moment someone has decided they want the paid thing. They now open a short prompt naming the model and offering to show the plans. Every locked row also carried the same sentence, "Upgrade to Pro to use Opus", so GPT-5.6 Sol and Fable 5 both claimed to be Opus; each row now names itself. Worse, a free account could be SITTING on Opus 5 with every message failing: the check that snaps your selection back to something available only asked whether the model still exists, not whether your plan can run it. It now checks both and falls back to the best model your plan includes - and it waits until it knows which plan you are on before deciding, because running that check while your account was still loading would have quietly demoted a Pro user's Opus choice to Sonnet and remembered it.
- Asking for help was a dead end on the screen where things break. The inline error in chat offered only a dismiss X, while the crash screen and the Settings row each had their own differently-styled Discord link. There is now one support affordance, with the real Discord mark, in all three places - and on the phone it opens in the system browser instead of a login wall inside the app.
- Dialogs no longer fly in from the top-left corner. EULA, License, GitHub, Discord and Docs all threw their panel diagonally across half the screen on open, which matched nothing else in the product - sheets rise, the command palette scales in place, toasts settle upward. They now rise into position and settle.
- Two flaws in the password sign-in shipped hours earlier, found by auditing it rather than by a user hitting them. The lockout counter never decayed, so once an account had eight lifetime wrong guesses, every single later typo re-locked it for fifteen minutes, forever, with no way out but a successful sign-in the lock prevented. Failures now expire after thirty minutes, so a burst still locks and scattered typos never accumulate. And the lock was recorded against the account, which meant only real accounts could lock - so eight attempts told an attacker whether an address was registered, defeating the deliberately identical error message. It is now recorded against a one-way hash of the address, so an unknown address locks the same way and reveals nothing. Response times were also equalized for an account that exists without a password.
- Terms, Privacy and Refunds are back, and rewritten to be true. All three pages had been returning "not found" since mid-May, when the marketing site moved to a new framework and the routes were not carried across. Nothing pointed this out because the pages were still listed in the file we hand to search engines, and the only visible links were on the sign-in screen, in the app, and at the bottom of every documentation page - so every new user was asked to agree to terms they could not read, in all ten languages, and Apple requires a working privacy policy link inside the app. The old text was recovered rather than rewritten from scratch, then checked against what Foxl actually does, which turned up two statements that were simply untrue: it said conversations are never kept on our servers (Foxl Code keeps them, because a coding task runs for hours and has to survive you closing the app) and that your own provider keys never reach our servers (they pass through to reach the provider; they are not stored). Publishing that as-is would have replaced a missing policy with a misleading one. The new pages describe each product separately, name the transcription service meeting audio is sent to, list every company involved in running the service, and say plainly that we do not track you, do not sell your data, and do not train on your content. The in-app agreement also claimed Foxl "runs 100% locally" and that data "never leaves your device"; that is gone too.
- You can set a password now, if you want one. Sign-in was email links and Google, Apple or GitHub only. That is a good default, but it left no way to hand someone a credential they can type - which App Store review requires for the account they test with - and no second route in when email is slow or you are on a device without your inbox. A password is optional and additive: you set it in Settings once you are already signed in, so it can only ever be attached to an address you have already proven, and forgetting it needs no reset flow because the email link already does that job. Minimum twelve characters, a short memorable phrase rather than a short jumble. After eight wrong guesses password sign-in pauses for fifteen minutes while email links and connected accounts keep working, so someone guessing at your account cannot lock you out of it.
- The iOS app was carrying several things App Review would have stopped, one of which crashed it. Setting a profile picture, or attaching an image to a chat or note, opens the system file picker, and choosing "Take Photo" starts the camera - which iOS refuses to do without a stated reason, terminating the app instead. There was no camera reason string, so the first reviewer to set a profile picture would have watched Foxl quit. The app also shipped no privacy manifest, which Apple has required since May 2024 and which has to name the system features bundled libraries touch. Three purchase-shaped controls were present on the phone: a credit top-up card with three price buttons, an Upgrade button in the out-of-credits banner, and a checkout helper in the Notes client. All three were dead - they pointed at an endpoint the server does not have - so they did nothing, said nothing, and would have read as either a broken app or an attempt to sell around the App Store. Billing is now read-only on the phone with a single link out to the web, and the buttons that looked like purchases either manage an existing plan or are gone. The out-of-credits banner previously offered an Upgrade that silently failed for everyone, on every platform. On iPad, and on an iPhone held sideways, the notification settings disappeared entirely, so there was no way to turn any notification off. The AI disclosure sat in a line that is hidden on phones, so the iOS build shipped without one; it now appears in the empty chat where there is room for it. The end-user agreement claimed Foxl "runs 100% locally" and that data "never leaves your device", which is not true of the web, phone, Notes, or Code experiences - it now describes what actually happens and points at the privacy policy. The iPhone app also offered a macOS installer as its main button, and a leftover shop image contradicted our answer that the in-app game has nothing to buy.
- Search engines were being told the wrong thing about nearly every URL we publish. Google Search Console reported 108 pages redirecting, 38 not found, and a page both blocked and indexed at once; tracing each bucket to its source turned up bugs rather than noise. Old versioned docs URLs all collapsed onto the generic docs hub even though 147 of them had an exact current equivalent, so a decision to reorganise the docs quietly discarded the reputation of every page it moved.
foxl.ai/llms-full.txthad grown to 359KB by concatenating the docs corpus with the blog corpus, and because it carried no directive it was fully indexable - including 220KB of blog prose we had deliberately marked as not-for-indexing, laundered into an indexable file.changelog.mdserved a 342KB copy of/changelogthe same way. Every one of the 41 markdown twins on the docs site was in the same state, and docs had no mechanism to set a response header at all, because a Next.js static export silently ignores header config. Requests for a page that does not exist were given a redirect first and a 404 second, so one dead address was counted twice. The error page itself answered 200./docs/desktopand/docs/referencewere listed in the sidebar but had no page, so 18 pages advertised a breadcrumb trail through a dead link.app.foxl.aiforbade crawling AND asked not to be indexed, which cannot work: a crawler that is turned away never reads the second request, so the address stayed in the index with nothing in it. Seven blog posts existed only behind a click and so were invisible to any crawler. All of it is fixed, and the checks that run on every build now fail when a sitemap advertises a page that no longer exists - the gap that let three deleted pages be recommended to Google for two months. - Broadcast notifications can now reach a large audience safely. Sending to everyone used to happen in a single request that opened one connection per phone, held every device in memory, and recorded nothing - so past a few hundred devices it would be cut off partway through with no way to tell where it stopped, and retrying would notify everyone who had already been reached a second time. A broadcast is now a job that advances in bounded batches, remembers its position, and resumes on its own if it is interrupted. Each phone is sent to at most once, even across a retry, because that is recorded before the send rather than after. A send in progress can also be cancelled, which matters because a notification cannot be taken back once delivered.
- The second half of the push fix: the app now actually receives its notification token. v0.5.4 corrected the notification environment, which let iOS issue a token for the first time - and then the app dropped it, because the native code that hands the token to the notification plugin was missing entirely. So no device ever finished registering and the server still had nobody to send to. Confirmed by watching the server while the app ran: it made every other call it should and never once asked to register the device. Registration failures are now reported too, so a signing problem can be told apart from someone simply declining notifications.
Fixes
- Going back also takes the sidebar back. Switching from Agent to Notes and then pressing Back returned the page you were on but left the sidebar showing Notes, so the app disagreed with itself about which product you were in. It only got that right for three specific pages; every other one - Workspace, Agents, Schedules, For You - was missed. The product a page belongs to is now derived from the navigation itself, so it cannot go stale, and the choice is saved too - previously a reload right after going back would silently put you back in the product you had just left. Opening Settings still leaves your product alone, since it sits over whichever one you were in.
- A schedule run shows one back arrow, and says which run you are looking at. On the Mac there were two identical back arrows side by side, because the window header already has one. The title now names the run instead of repeating where the back button goes.
- Schedules and agents now notify your phone, not just your desktop. A schedule set for 6am told the desktop it had finished and nothing else - the phone stayed silent, even though the Notifications screen has had a Schedules toggle since it shipped. The toggle was real but nothing ever read it: the desktop broadcasts one notification event for schedule runs, finished agents, heartbeat results and feed items, and that event was never forwarded to the phone. It is now, under the toggle you already had, and failures come through under the same one - a scheduled job that quietly fails is worse than one that tells you. Tapping opens the conversation it came from, in the app.
- Notification taps stay inside the app. Tapping a notification that pointed at a Foxl screen used to leave the app and open the same screen in Safari, as a separate signed-in copy. Links to genuinely external pages - the changelog, docs, pricing - still open in the browser, which is what they should do.
- Dark mode is no longer flat black. The page, cards and dialogs were all the same near-black value, so nothing had any depth and only borders separated a card from the canvas behind it - and it sat next to a sidebar that was already blue-grey, so the seam between them was visible. Dark surfaces now share one hue with a step for each layer: page, card, dialog. Pure black against near-white text is also tiring to read for long stretches, so the canvas is lifted slightly; measured contrast stays well inside the accessibility standard (18.1:1 for body text, 7.5:1 for secondary text). Applies to desktop, web, iOS and the Notes app.
- Launching no longer stalls on the boot screen and then shows the server-log page. A healthy launch could sit on the loading animation and hand you the diagnostics screen - server log, restart button, "restarts 0/3" - about seven seconds in, even though the server was up and answering the whole time. The boot screen polls until the server is healthy and then navigates to the app, but it also checked that the window was still showing the boot page, and that check read an empty address while the boot page was still being committed. An empty address is not "the user navigated away", it just means "not attached yet" - so the poll stopped for good, and the only thing left that could change the screen was the boot page's twelve-second escape hatch to the diagnostics view. Measured: the address usually commits in about 100ms but was seen taking 794ms on a cold first run, so this was an intermittent race that got worse the slower the machine. Versions before the boot screen existed could not hit it, because they waited for the server and then opened the app directly.
- Cmd+Shift+F in Foxl Notes no longer opens two search surfaces at once. The same bug was fixed for Chat previously, but the Notes branch was missed, so on the Mac desktop the recordings palette and the title-bar search dropdown both opened stacked on one keypress. Notes keeps the palette everywhere the title bar is not already showing a search field, which is web, iOS and Windows.
- "Open HUD" appears once in the Notes header, not twice. In a narrow window the action moved into the overflow menu but the standalone button stayed, so the same action was offered twice in one row.
- Screen readers announce the right thing when closing things in Notes. The buttons that close the meeting HUD window, the HUD's AI panel and the Notes settings page all announced "Close transcript", because they reused the transcript's label.
- The floating "Back to Notes" recording pill is translated. Its four strings were the last hardcoded English in Notes navigation, in an app that ships ten languages.
- Push notifications on iPhone can work at all now. Every TestFlight build so far, including the most recent one, was signed for the notification sandbox rather than for distribution, so iOS never issued the app a notification token and the server had nowhere to send anything. No push of any kind - a finished chat, a Code task, a Notes summary - has ever been able to reach a phone. Nothing in the build or upload process could reveal it: the build uploaded normally and Apple marked it valid, because that check looks at the app package rather than at whether notifications function. Three things had to be corrected: the notification environment was declared as sandbox, the project pinned the Release build to a development signing certificate, and the account had no distribution certificate at all. The shipped app is now verified to carry the production notification environment and a distribution signature before it is uploaded. This needs one real device to confirm end to end, since a simulator never issues a notification token.
Fixes
- The window can be made smaller. Its minimum was 800x600, but on a 14" Mac with Accessibility > Larger Text turned on the screen only leaves about 560px of height once the menu bar and Dock are accounted for - less than the minimum - so the window could not be shrunk to fit and its bottom edge, including the composer and send button, sat under the Dock with no way to reach it. The minimum is now 640x480.
- The app stops draining your battery while it sits idle. macOS listed Foxl under "using significant energy" even when Activity Monitor showed almost no CPU, because the conversation list's loading spinner animated forever whenever there were more chats to load - which, with a long list, is always. An endless animation makes the window recalculate its styles every single frame, and that steady per-frame work is exactly what macOS counts as energy. Measured on an idle window: 1196 style recalculations per 10 seconds before, 0 after, and main-thread busy time dropped from 9.6% to 0.0%. The spinner now only spins while a page is genuinely loading.
- Upgraded the agent SDK to 1.11.2, which includes an upstream fix (contributed from this project) so a failed model stream surfaces a real error instead of finishing as an empty successful answer. Context-limit failures from Bedrock Mantle are now classified correctly and trigger context reduction rather than a silent empty reply.
- The phone stops burning battery when the desktop is offline. Two status dots pulsed forever whenever the desktop tunnel was disconnected - which on a phone is the normal state - and an endless animation makes the screen recalculate every frame. They are static now; the colour already said what the motion was repeating. Measured on a real iPhone: one infinite animation before, zero after.
- The Foxl mark on dark screens is a soft grey rather than the near-white it briefly became, so it reads as a logo instead of the brightest thing on the screen.
- Onboarding no longer appears in the iOS app. It existed to explain the desktop agent and offer a download, neither of which applies once you are holding the app - sign-in is the first screen now. The web app still shows it, where the download matters.
- Onboarding buttons picked up the app's rounded shape.
- The sidebar collapse button moved up into the window titlebar, beside the traffic lights, and a back arrow took its place in the page header - each control now sits in the bar it acts on. The sidebar header also has more room around it, so the logo, product tabs and navigation read as three groups instead of one crowded block. On phones and the web app the sidebar button stays where it was: there is no titlebar to move it to, and the drawer is the primary navigation there.
- The desktop search bar is a command palette now. Alongside conversations it searches your pages, every Settings pane, and your workspace files, so "where is the notification setting" is one query instead of a hunt - picking a settings result opens that exact pane. The filter is a set of checkboxes (Titles, Messages, Workspace, Commands) rather than a single choice, so you can narrow to two sources at once.
- Search finds individual settings, not just the page they live on. Typing "only when in background" now returns "Notifications > Only when in background" and takes you straight there - 51 individual settings across 12 panes are searchable, each shown with the pane that contains it so you can see where it lives before you click. You can search a setting's explanation as well as its name, so "not focused" finds the same toggle. Every one of them is searchable in all 10 languages, because the index stores which setting it is rather than its English text.
- Cmd+Shift+F opened BOTH the titlebar search and the popup palette on the Mac desktop, stacking two search boxes for one keypress. The titlebar field owns the shortcut there now; web and iOS keep the popup, which is their only search surface.
- A crashed screen can now report itself: "Report on Discord" copies the error, the screen it happened on, and your build to the clipboard, then opens the Foxl Discord so there is something to paste. Nothing is uploaded automatically.
- The header's duplicate "search all conversations" button is gone on the Mac desktop, where the titlebar already holds a search field. It stays on web, Windows and mobile, and comes back if you switch the titlebar to the status HUD.
- The boot screen is just the Foxl mark and a progress line now - the wordmark under it was set in a display style that appears nowhere else in the app, so the first thing you saw looked like a different product than the one it opened into. The mark is also legible on a dark desktop now (it was a near-black shape that vanished into a dark wallpaper behind the glass, leaving only a small blue sliver).
Fixes
- Light / Dark / System in Settings works. Picking a theme did nothing on the desktop app - it always snapped back to whatever macOS was set to, so on a light Mac the app stayed white no matter what you chose. The theme preference now has a single owner instead of one copy per screen, so the choice sticks, survives leaving and reopening Settings, and "System" still follows the OS.
- The Foxl mark is visible in dark mode. It is a near-black shape, so on a dark background it measured 1.1-1.5:1 (3:1 is the minimum for a graphic) and all you could see was the small blue wedge floating in the dark. The sidebar, sign-in, onboarding and Settings now use a dark-surface version of the SAME artwork - identical shape, same blue, just a body light enough to see. This was always broken in dark mode; the Appearance fix above is what made it visible.
- Two unreadable labels in the Foxl Den (a "last seen" timestamp and the dismiss hint on a rare-critter reveal) were faded to 2.3-3.2:1 against their background, under the 4.5:1 minimum for small text. Both are full-strength now.
- The titlebar HUD's "streaming" label no longer uses the brand accent colour or all-caps: the accent is reserved for things you can click, and all-caps reads slower.
- Secondary text is legible everywhere, not just on white. The grey used for every hint and description was tuned against a white page, so on the tinted sidebar and on selected rows it fell under the readability minimum. It is now a shade darker, verified against every light surface it can land on.
- Small print in Settings is easier to read. Descriptions and helper notes were set at a line spacing meant for single-line labels, which made the ones that wrap to two or three lines hard to track. Settings, Notifications and the error screen now use proper body spacing.
- Toggles are easier to hit. Switches look the same but now have a full 44px touch target instead of 20px, and the sidebar's sign-in and account rows were under the minimum too.
Features
- The desktop window is real frosted glass now. On macOS 26+ the app's outer chrome - the canvas the sidebar sits on - is a native NSGlassEffectView, so your desktop shows through it the way it does in Outlook and Codex. The inner content card stays opaque, because that is what you read on. It degrades to exactly today's opaque window on Windows, Linux and older macOS.
- Starting the app shows you something. The packaged app used to wait up to 30 seconds for its local server with NO WINDOW ON SCREEN at all - a Dock bounce and then nothing. There is now a glass boot window from the first moment, which hands over to the app the instant the server answers, and offers the log/restart diagnostics if boot is genuinely stuck. Mobile and the web app got the same treatment for the gap before the bundle mounts.
- The glass firms up once the app is up. While loading it is genuinely see-through, because there is nothing to read yet; once real UI is on screen the surface steps up so small labels stay legible over any wallpaper.
- A key colour, and it is not blue-by-accident any more. Royal blue and gold, sampled from the plush Foxl artwork, with the surfaces carrying a pale blue-grey hint of the same hue on desktop AND on iOS. Every value was checked for contrast on every surface it can land on rather than picked by eye.
- Date dividers in the conversation list. Scrolling reveals a rounded date pill between the rows - Today, Yesterday, then dated days for the past week, then by month - and hovering keeps them up so they do not flicker. The space collapses when they hide, so a still list has no gaps. Phones keep the floating pill instead, since touch has no pointer to rest on the list.
- The date groups are now mutually exclusive. "Previous 30 days" and "June" could both be true for the same conversation, so which divider it appeared under depended on ordering rather than on its date.
- Appearance moved into Settings > General, and works everywhere. It had been placed inside an Electron-only block, so the theme switch did not exist at all on iOS or the web app - and its labels were showing as raw keys. Launch-at-login and the titlebar HUD are now under their own Desktop heading rather than filed under Updates.
- The sidebar has its own error boundary. The one that existed wrapped only the page content, so a crash in the sidebar unmounted the whole app and left a blank window with no error card - which is the failure that boundary exists to prevent.
- Search remembers what you opened. An empty search box now lists the conversations you last opened from it, newest first, instead of a line of instructions - kept on the device, since a recents list that followed you between machines would surface work you did somewhere else. There is also a filter for narrowing to title or message matches; it is limited to what the search endpoint can actually answer rather than pretending to more.
- The sidebar reads as navigation, not as a toolbar. The leading icons are gone from the nav rows - a label was already there, so the icon was decoration - while Settings and the account row keep theirs, because those are rows you hunt for rather than read. The collapsed icon rail is unaffected.
- One selection at a time. The Chat row and the open conversation were both highlighted, which read as two selections; Chat now defers to the conversation and is only highlighted when it IS the destination. The selected row also has a shade you can actually see - measured, the old one was 1.002:1 against the new sidebar surface, i.e. invisible, which is why font weight was doing all the work.
- The theme switch moved out of the sidebar footer into Settings > Appearance as three labelled choices. That footer slot is always visible, so it now holds New Chat - the frequent action - rather than a preference you set once.
- The titlebar search opens downward, like Outlook's. A real field - bigger type and a taller box - that expands on focus and unrolls its results beneath it, with arrow keys and Enter. The centred command palette stays exactly as it was, because the web app and iOS have no titlebar to put a field in and still need it; the header's search icon keeps opening that.
- One surface token, so nothing can mismatch. The sidebar, the reveal drawer and the swipeable chat rows all derive from the same value now. They did not before: the drawer and the rows painted
--background, which read as a white block sitting on a tinted sidebar in both light and dark. - The loading screen is translucent on a glass window instead of flat white, so the material is visible at the moment there is nothing else to look at.
- The titlebar centre is a search bar. That is the most-looked-at space in the window, and it was spending it on a status readout. The live agent HUD is now a setting; status itself moved next to the notification bell, so it is still one glance away.
Fixes
npm run devactually starts the app. It brought up the agent server but not the web server, so unless you separately remembered to run Vite the window opened on a blank page with a connection error.- Text that was too faint to read is readable. 75 labels were drawing
muted-foregroundat reduced opacity, which measures 1.96:1 to 3.23:1 against white - the floor for text under 18px is 4.5:1. De-emphasis now comes from size and weight, which is what it is for. - Things you have to click are big enough to click. Measured on the real rendered app, the desktop had 53 tap targets under 24px; the conversation delete button was a 16px square and the connection dot an 8px one. Now 2 remain and both are deliberate (a full-width row and the full-height sidebar drag rail).
- Raised 94 labels that were 9-10px to an 11px floor and 9 icon buttons from 20-24px to 28px. Pixel-art surfaces in the game are deliberately exempt.
- Dropped the "For You" group heading that sat directly above a nav item also called "For You", and stopped the mobile suggestions label shouting in all-caps.
- The game is a real game now: a home screen, a collection, and it saves to the server. It opens on HOME - who lives with you, what you hold, what to do next - and the summon becomes somewhere you go from there rather than the screen you land on. FOXLS is the collection: every character has a card, and the ones you have not met are still shown as silhouettes, because the gaps are the point.
- Nine plush-doll characters, each with its own pixel version. A patchwork ragdoll, a stitched fox, two bunnies, three cats, a puppy, a husky and a top-hat bear. Every one has a hand-drawn illustration for the summon and the collection card, plus a 16x16 pixel version OF THAT SAME DOLL for walking around the den - so the thing you pull is visibly the thing that moves in. Their names and lines are translated into all ten languages.
- Your collection survives losing your browser. The game used to live entirely in local storage, so clearing site data destroyed a collection built from weeks of real agent work, a new phone started from nothing, and a reinstall quietly re-granted the paid-plan gems. It now syncs to the server, and the merge is careful in the way that matters: what you have collected is unioned so nothing is ever lost, and currency is never added across devices, so owning two phones cannot mint gems.
- A pixel mini-game, where Quests used to be a dead tile. Your Foxls pop up one at a time and you tap them before they wander off - thirty seconds, no fail state. Catching them raises your bond with the ones you have neglected, which is the one thing in the game that cannot be bought or rushed. Only characters you have actually summoned appear.
- The den can tell you when someone wanders in. At most once a week, off by default, and only if you have actually played - tapping it opens the game. It is the second notification category you have to opt into, alongside promotions, because nobody asked for it.
- Nothing in the game is for sale, and both currencies are earned by working. Sparks come from your agents' tool calls, as before. Gems now come from your actual token usage - roughly one per 250,000 tokens, capped at a few a day - instead of arriving with a paid plan, and the shop is gone entirely. There is no way to buy either currency and no way to turn credits into pulls, in either direction. Home has a plain explainer that says so, and the pull odds are still published in-app.
- The den is a real room. An arched window with the moon behind it, a wooden floor in perspective, a rug, swaying lanterns, sakura - and everyone you have ever met wanders it at once, pausing and turning to face where they are going. Tap one to spend time with it; the bond it builds is the one thing in the game nothing can buy.
- The game no longer takes the Agents page away from you. Opening it used to unmount the real page, so anyone with agents running lost the list, the run bar and the live state - and got them rebuilt from scratch on the way out. It is a layer over the page now, and the page keeps running underneath.
- The game is off by default. It was on for everyone, which made it not an experiment: a game-controller chip sat in the header of a tool that bills real inference credits, with no way to have opted in. Turn it on in Settings > Experiments.
- "Promotions and tips" now actually does something, at most once a month. The toggle existed with nothing behind it. If - and only if - you have turned it on, you may now hear from us about the parts of Foxl you use, no more than once every 30 days, and never in your first week. Which message you get is based on counts and dates (your tier, when you joined, whether you use Notes or Code), never on the contents of your messages, transcripts or files. It remains the only notification category that is off until you ask for it.
- Foxl Notes tells you when the summary and the report are done, not just the transcript. Those two are the slowest things Notes does - they read the whole transcript - and they usually run unattended at the end of a recording, so they were the two most worth hearing about and the only ones that stayed silent. A summary you watched appear does not notify you; one that landed while you were elsewhere does.
Fixes
- The For You headline shows its number again, in every language. It was built by gluing a count in front of a translated fragment, which silently forces English word order on all ten locales - and Korean attaches its counter suffix to the numeral, so it came out as a bare "개 항목이 대기 중입니다" with no number in front of it at all. One of the strings was passed no count whatsoever, so even English read "items waiting for you". Every hero string is now a whole sentence that carries its own number.
- The sidebar drawer stops re-frosting the whole screen while it slides. A backdrop-filter re-samples its backdrop every frame, and the composer's frost was paying that for the entire 360ms of the reveal animation - the most expensive per-frame work there is in a web view, and the reason the drawer stuttered on a phone but looked fine in a simulator. It now mattes for the transition and re-frosts at settle, which is the same thing the keyboard dock has always done.
- The "..." menu opens in one continuous motion. Its liquid growth was measuring the panel with a method that returns the *scaled* box, while the panel was running its own scale animation - so the easing was applied twice over and the shape sat nearly still for the first 60ms before lurching. It now measures the untransformed size, and sends half as many messages to the native layer per frame.
- The game no longer draws over the Settings sheet. Both were at the same stacking level, so which won came down to which mounted last - and the game, being portaled, always did. It sits below anything you deliberately opened now.
- The portal glow is translucent, and its opacity control works at all. The pulse animation hardcoded its own opacity, and an animated property overrides the element's own - so every attempt to dim the portal was silently ignored (the markup said 0.14 while the browser computed 0.88). The peak now comes from a variable the caller sets.
- The collection screen is symmetric. The title ribbon was centred in the space left over beside the back button rather than on the screen, and the sort control was stealing width from the four rarity filters, which left the whole row sitting left of the grid beneath it.
- Two dead-end "it's quiet" screens are gone. An empty den now goes straight to the summon rather than showing a screen whose only content was a signpost - and which drew an animal that is no longer in the game. The summon's own empty state got a readable plate instead of thin text over the brightest part of the portal.
- The game opens on the first tap. Tapping the chip before the game had finished loading did nothing - and left it convinced the game was already open, so the next tap closed it again. The first tap now works whenever you make it.
- Escape, Android back, and the iOS edge swipe all leave the game. Previously the only way out was to find the small close button.
- The game clears the desktop title bar. Its top row - level, sparks, the close button - was drawn underneath the macOS title bar, where it could be neither read nor clicked.
- Sparks accumulate while you are looking at them. The loop that mints them skipped running whenever you were on the Agents page, handing off to a foreground loop that no longer existed - so the currency froze on precisely the screen that spends it.
- Presentations and documents show a real rendered page, not just their text. Opening a .pptx or .docx gave you its text content, which is genuinely useful and stays - but it could not show you what the file actually looks like. On macOS the desktop app now renders the real page (the same renderer Finder uses for spacebar preview) and shows it above the text. No extra download and no new dependency; on other platforms the text view is unchanged.
Fixes
- Images and Office files open in the desktop app. Every image, PDF and Office file failed to display in a released desktop build while working perfectly in development. Production builds protect the local server with a token that the app attaches to its own requests - but an image tag cannot attach anything, so every one of those requests was rejected. They now carry the token a way a tag can.
- A file that cannot be displayed says so. It used to show a spinner forever, which is why the problem above went unnoticed for so long: a rejected file and a slow one looked identical. A failure now explains itself and offers to open or download the file instead.
Features
- Foxl Notes: importing a recording now shows its progress. Picking an audio file used to give no sign that anything was happening - transcription runs at the speed of the audio, so a long file looked like the app had simply ignored you. There is now a progress bar with the file name and a live percentage, an explanation that it takes about as long as the recording, and a confirmation at the end naming the note it created (with its length and segment count) plus a shortcut to the folder on desktop.
- Foxl Notes: you can import a recording on a phone. Importing previously needed either drag-and-drop or the desktop File menu, so it was unreachable on mobile even though the transcription itself worked there. There is now an "Import a recording" row on the Notes home screen.
- The Agents game is a real game screen now. Flipping the game switch takes over the whole screen instead of showing a small panel under the form: a summon view with your level and currencies along the top, the character you pulled on a light burst with its rarity and star rating, its own line of dialogue, and a six-tab game nav along the bottom. Pulling again is animated - the character lands with an overshoot, the stars snap in one by one, petals fall. Three original Foxl characters at different rarities. The switch itself moved out of the button row and onto a glass chip in the header row - the same place, size and material as the chat menu's chip, and real iOS glass rather than a CSS imitation. (Presentation only: level, gems and your collection are kept on this device, pull odds are published on the screen, and pulls cannot be bought - there is no purchase path and nothing is charged or synced.)
- Foxl Notes can notify your phone when it finishes. Notes was the only product without notifications, and the one that needed them most: transcribing an imported file takes about as long as the audio, so the natural thing is to start it and put the phone down. A "Notes transcripts" toggle joins the others under Notifications (on by default), and a finished import now reaches you even if you have looked away.
Fixes
- Workspace: PNG, DOCX and PPTX open on the web app. They spun forever while the exact same files opened fine from the desktop, because the tunnel sent the text
"null"as a request body for every file fetch - which the desktop server rejected as malformed JSON and answered 500 to, before the file route ever ran. Verified end to end: all three now arrive byte-valid, including files in folders with spaces. - Menu text is readable in dark mode. The native menu rows and the "..." glyph were drawn in the system's label colour rather than the app's, so in dark mode they came out near-black on dark glass.
- The search palette has no stray close button. It had nowhere to sit that was not on top of the search field; Escape and a click outside already dismiss it.
- Notes sidebar rows are no longer clipped under the selection.
- Push notifications can be delivered at all. No device had ever registered, so every notification - chat, Code and Notes alike - was silently going nowhere. The device token arrives from Apple once per install, almost always before you have signed in, and it was being discarded in that case and never offered again. It is now kept and uploaded as soon as sign-in happens.
- An idle app no longer burns through the request budget. Two open clients could disconnect each other in a tight loop, and because each connection briefly succeeded the retry backoff reset every time and never backed off at all - tens of thousands of requests a day with nobody using the app. Backoff now only resets for a connection that stays up.
- The chat menu opens below the button in a browser. It overlapped the button it came from, so the click that opened it landed on the first menu row. On iOS the overlap is kept deliberately - that is what makes the menu and the button read as one piece of glass.
- Header chips fade out with a sheet instead of blinking away. They disappeared instantly while the sheet was still sliding in, which is what made the transition noticeable; they now dissolve on the same timing the sheet arrives with.
- A dialog close button no longer floats below the search field.
- The Code-tasks notification toggle actually saves now. It was sent under a name the server did not recognise, so switching it off appeared to work and quietly reverted the next time the screen opened.
- Two open clients no longer knock each other offline in a loop. Every web client identified itself to the relay with the same hardcoded name, and the relay keeps one connection per name - so a second tab (or the phone alongside a tab) disconnected the first, which reconnected and disconnected it back, forever. That was the constant network traffic visible in an idle app. Each client now has its own identity, so a reconnect only ever replaces its own connection.
- Provider and model names are readable on a phone. Settings showed "foxl..." and "Opus..." because the status badge beside the name kept its full width while the name itself collapsed. The name is now the last thing to give way.
- Workspace: images, slides and other binary files open instead of loading forever. A PNG or PPTX preview span forever and then reported "The transfer from your desktop stalled", on the web app as well as the desktop. The desktop server had no 404 for an unknown
/api/*path at all - it left the request open, so nothing downstream ever learned the request had failed: the tunnel waited on a reply that would never come, the relay held an already-accepted empty response for ten minutes, and the viewer sat on a spinner until its own timeout blamed the transfer. Unknown routes now answer immediately, and the tunnel's streaming path has the same first-response deadline its non-streaming sibling always had (long-lived subscriptions are unaffected). - Conversation search is the same bottom sheet as everything else on phones. It was a top overlay unlike Settings and the model picker; it now rises from the bottom as the same floating card, and can be dragged up to full screen for long result lists.
- The chat menu's opening animation matches the rest of the app. It had its own springy curve plus a second overshoot in its keyframes, so it felt rubbery next to the sidebar and the sheets. One easing now describes all of them.
- The whole menu row is tappable again, not just its text. Hiding each row's icon also removed that part of the row from touch handling, so the left third was dead.
- The desktop really does stop repeating "Web client connected securely". The earlier fix deduplicated by device, but the secure-handshake path reported an id derived from a fresh key on every reconnect - so an idle tab looked like an endless stream of new devices. Measured: 240 reconnects over two hours now produce one notification, and a genuinely new device is still announced.
- The chat menu's items are clickable again (iOS). "Find in this chat", "Search conversations" and "New chat" all did nothing when tapped. Two independent causes: the menu's rows were being hidden with
visibility, which removes them from touch handling as well as from view, and the native glass container was claiming every touch inside the region it covered rather than only the ones its own controls wanted. - The chat menu no longer closes the instant you open it. On phones the panel was positioned far enough up to sit under the finger that opened it, so the same tap landed on a menu row - the menu opened, silently ran "Search conversations", and shut again. Measured the safe range with a real touch and the panel now stops short of it.
- The chat menu opens as one glass shape with its chip, and lets go again. The merge was a chip joined by an empty spacer to a separately-styled panel, with a visible seam where the two met; the panel is now part of the same shape, so it grows out of the chip and retracts back into it. The web hamburger icon no longer flashes underneath as the menu closes.
- The in-chat "Find" bar has its own row on phones. It was a small pill in the top-right corner, overlapping the title on one side and sliding under the menu chip on the other. It now sits below the header at full width, with touch-sized buttons.
- Search and dialogs clear the status bar on phones. The conversation search sheet opened with its field partly behind the clock, and a dialog's close button was a 16-point target inside the status bar - too small and too high to press. Both now respect the safe area, and close buttons are full-size touch targets.
- Foxl Notes: importing a file always creates the note. A recording that read fine but contained no recognized speech - a silent stretch, music only, or audio in a different language than the transcription setting - produced a message and nothing else: no note, no kept audio, no way to retry without picking the file again. The note is now always created with its audio attached, so an empty transcript is something you can edit or re-run instead of a dead end.
- The desktop stops announcing the same web client over and over. A browser tab or phone left open reconnects on its own for ordinary reasons, and each reconnect produced another "Web client connected securely" notification, so notifications kept arriving while nobody was doing anything. A device is now announced once, and its reconnects are silent.
- Foxl Code settings: the save bar matches the panel it sits in. It was painted pure white, which on the mobile Settings sheet read as a strip pasted across a differently-tinted surface, and it now also keeps clear of the home indicator.
- Foxl Notes: a file with no speech says so. "Could not read the file" and "the file was fine but nobody spoke" were reported as the same failure, and a silent or music-only recording therefore looked like a broken import. They are now separate messages, and the one for no speech suggests what to check.
- iPhone: the header buttons stop disappearing when you open a chat. Entering a conversation, or following a link into one, made the sidebar and menu buttons vanish - and only in Foxl Agent, which is what made it look random. Moving between screens inside the app looked like a page reload to the glass layer, so it threw the buttons away while the app still believed it had them, leaving an empty gap where each control was. It now tells a real reload apart from moving around inside the app, so the buttons survive both.
- iPhone: one set of buttons, not two overlapping. On the Foxl Code Tasks screen the old CSS imitation was still being drawn underneath the real iOS material, so the buttons showed as two stacked shapes. Both surfaces now go quiet the moment the real material takes over, in every product.
- iPhone: the chat menu grows out of its button as one shape. The menu and the button it came from now fuse into a single piece of glass, the way iOS menus do, and the menu's labels stay perfectly readable. Previously the shape sat offset from the menu it belonged to and left an empty ring where the button was.
- iPhone: swipes work in both directions. The sidebar could only be swiped open and the terminal only swiped closed, so the other half of each pair needed a button. Now: swipe in from the left edge to open the sidebar, swipe left to close it, and swipe in from the right edge to open the terminal.
- The sidebar shows five and a half conversations. Half a row stays clipped at the fold, so it is visible that the list continues.
- Foxl Code: the regenerate and edit buttons are gone from replies. They were drawn on every message but did nothing when tapped, because Foxl Code has no re-run or edit-and-resend behind them. They will return when it does.
- Foxl Code stops hanging on "Loading..." when its server is unreachable. Requests had no deadline, so a server that accepted the connection and then went quiet left every screen spinning with nothing to report. Requests now give up after 30 seconds and say what went wrong, and cancelling still cancels immediately.
- iPhone: the app uses the whole screen. The top and bottom strips used to be native chrome the page could not draw into, so backgrounds and sheets stopped short of the physical edges and left a flat band along the home indicator. The page now owns the full display corner to corner and keeps its own clearance for the Dynamic Island and the home indicator, so everything runs to the edges like any other iOS app.
- iPhone: bottom sheets meet the screen properly. The model picker and Settings drew their side edges and square bottom corners inside the screen, so a sheet looked like a slab pasted over the app instead of a panel floating on it, with a bright band left under it along the home indicator. Sheets are now rounded on all four corners, lifted clear of the bottom edge, and genuinely frosted, and the strip below them follows the sheet instead of the page. The section headings inside the model list no longer paint white bars across the frosted surface, and iOS no longer draws a stray blue outline around the sheet or the selected row.
- iPhone: the header buttons no longer flicker or shift. They used to swap between the real iOS material and a CSS imitation whenever a menu, sheet or the sidebar was open, and the two treatments do not look alike - that swap was the shimmer and the little jump. The buttons now keep one presentation at all times. They also carry a hairline edge, so the shape stays visible over a white page, where glass has nothing to refract, and they follow light and dark mode properly instead of flashing bright.
- iPhone: the sidebar button is a sidebar icon, the same one the desktop app uses for it, and the chat menu sits at the right edge of the header again.
- The conversation title no longer overlaps the chat on a phone. The mobile header is transparent so the transcript scrolls under it, which left the title printed on top of the message text. It is gone on phones (the desktop header has its own surface to sit on and keeps it).
- Two more messages fit on screen, in Foxl Agent and Foxl Code. Both transcripts padded the top against a transparent header they already scroll beneath, and over-reserved at the bottom for the floating message box - wasted height on desktop and mobile alike.
- iPhone: the chat menu opens smoothly. The
...menu sprang from a point much smaller than the button and animated a blur on a frosted surface, which made it judder on open; it now unfolds from the button itself using only compositor-friendly motion, with a softer directional shadow. - Foxl Code: the chat inside a task scrolls again. Open a task, let the conversation run long, and the transcript was simply cut off with no way to scroll to it - and the message box would not stay at the bottom of the screen where it belongs. Both came from the same layout mistake, and both are fixed. The scrolling transcript is now literally the same component Foxl Agent uses rather than a second copy of it, so the two products can no longer drift apart like this.
- Errors arrive as a reply, not a red bar. Running out of credits showed you
402 {"error":"Credits exhausted","remaining":0}in a detached red strip while the conversation itself went blank, as if nothing had happened. Every failure in Foxl Code now ends the turn inside the conversation with a plain sentence and a next step ("Your credits are used up... Add credits or wait for your monthly refill in Account"), keeping any partial output that had already streamed. Foxl Agent already did this; both products now use the same code for it. - Foxl Code: the first bubble on a task is the right width. The prompt that seeds a task page rendered edge to edge across the column instead of sitting in a normal user bubble like every message under it, which is what made Foxl Code chat look wider than Foxl Agent. It was skipping the shared message wrapper that applies the width, so it also failed to line up with the real turns below it.
- Foxl Code: task status lives in the top bar, not in popups. Cancelling a task, retrying it, resuming it, or marking it done each threw a toast over the app - and a toast saying "cancelled" could sit next to a header still reading "Running". Those eleven popups are gone. The outcome now appears next to the status pill it describes and fades on its own, so the top bar is the single place that tells you where a task stands. The header also no longer claims a task is running Opus 4.8 while its real model is still loading.
- Foxl Code: Cmd+N starts a new task. It used to throw you out of Foxl Code and open a new Foxl Agent chat instead. Find and Search from the File menu now reach Foxl Code's own search too.
- Korean greetings no longer read like machine translation. The greeting on an empty chat is written fresh by a model each time, and it had been translating the English phrasing word for word, which produced stiff results in Korean. It now writes original copy in your language.
- Foxl Code: New task actually clears the chat. If you were already on a fresh chat (not inside a task), pressing New task left the whole previous conversation and any error on screen - the cursor moved to the message box and nothing else happened. It now empties the conversation the way New conversation does in Foxl Agent.
- Web search is now Web Access, in Settings. The globe in the message bar was labeled "Web search" but actually switched Foxl's control of your Chrome browser on and off - a standing permission sitting in a per-message spot. It moves to Settings > Web Access, which gathers everything in one place for the first time: browser control, fetching pages by URL, the Chrome extension (including how to install it, which the app never told you), and the list of domains Foxl is allowed to visit.
- You can remove a domain from the allowed list. The list only ever grew. Removing an entry took it off the screen but left the site reachable, because an allowed domain always won over the blocked list. Removal now actually removes.
- Workspace opens every kind of file, in a real code editor. Only markdown could be opened before - anything else was handed off to Finder, which on a phone meant tapping a file did nothing at all. Now images, PDFs, audio, video, Word documents and spreadsheets all open in the app, and text files open in Monaco, the same editor that powers VS Code: syntax highlighting for 80+ languages, find and replace, folding, multiple cursors. On a phone it is tuned rather than shrunk - larger type, wrapped lines so nothing scrolls sideways, and no minimap eating the screen.
- Agents, Schedules and For You lost the giant number. Each page opened with a 60-pixel digit that was usually 0, under a shouty repeat of the page name that the header already showed, with the same counts printed again right beside it. On the Agents screen the word "Agents" appeared three times. All three now open with one compact line, so the thing you came for is on screen instead of below it, and a count of zero simply is not shown.
- Foxl Code and Foxl Agent now share their tool and message code. The two products had separate copies of the code that decides a tool's icon, title, and output formatting, and separate copies of the message shapes underneath the chat. The copies had quietly diverged, and each difference was a real defect: a live "progress" readout that only Foxl Code could show, and message block types that only Foxl Agent actually type-checked. There is now one implementation of each, so a fix to one product is a fix to both.
Features
- iPhone: the header buttons are real iOS 26 Liquid Glass. The hamburger, the chat menu, and the scroll-to-bottom button are now genuine UIGlassEffect rather than a CSS imitation, so they refract the page behind them and react to touch exactly like the system's own controls, with their icons drawn crisply on the glass. The message box deliberately keeps its CSS glass: native glass refracts everything inside its own bounds, so the placeholder, the model picker and the send button would have been smeared along with the background. Glass also steps aside automatically whenever a menu, a sheet or the sidebar drawer is open, so nothing can float on top of them.
- Claude Opus 5 is here, and it is the new default. Anthropic's most intelligent model is available across Foxl Agent, Foxl Code, and Foxl Notes from day one. It matches or beats Fable 5 on agentic coding and professional knowledge work at half the price, so it takes over as the default everywhere Opus 4.8 used to be - same $5/$25 rate, materially stronger results, no action needed on your side. 1M context, 128K output, all five reasoning effort levels, and reasoning is now on by default: Opus 5 decides for itself how long to think on each turn. Every earlier model stays available in the picker.
- Model pickers show the current generation, not the archive. Superseded models (Opus 4.7 and 4.6, Sonnet 4.6, GPT-5.4 and older, Gemini 2.0) are now hidden by default across all four model pickers - chat, Settings, Foxl Notes, and Foxl Code. Nothing was removed: "Show older model versions" in Settings > Model & provider brings them all back, and every picker has the same one-tap row at the bottom of its list. A model you have selected or favorited is never hidden, and searching still finds everything, so typing "4.6" turns up Sonnet 4.6 with the setting off. Foxl Notes and Foxl Code settings carry the same switch, so wherever a list is shortened you can widen it on the spot.
- Foxl Code: your starred models stop disappearing. Foxl Code and Foxl Agent share one favorites list, but stored them in two different formats, so opening Agent chat once silently unstarred every model you had favorited in Code. New Foxl Code chats also started on Sonnet 4.6 while the send path assumed Sonnet 5; both now agree on Sonnet 5.
- Gemini CLI actually works now. Replies came back empty every time: Foxl read the CLI's stream in a format it has never emitted, so every line of the answer was discarded. Errors were worse than silent - a quota or sign-in failure was indistinguishable from success, so you got an empty reply with no explanation. Both are fixed, the current Gemini 3 models are selectable, and signing in with
geminiis now detected (Foxl only looked for an API key, so a signed-in CLI still showed as not configured). - Fixes to model identity that were quietly costing you. Picking the short "Claude Code Sonnet" option ran Sonnet 4.6 long after Sonnet 5 shipped. Six other model IDs resolved to nothing internally, which made Foxl treat a 1M-context model as 256K - compacting your conversation about five times earlier than needed - and record its cost as zero. GPT models over a ChatGPT subscription were assumed to hold 1M tokens when the service actually serves 272K, so long sessions failed upstream instead of compacting in time. A GPT-5.3 option that OpenAI does not serve at all has been removed.
- Settings: a Refresh button next to Save Provider Settings. After running
claude /loginorcodex loginoutside the app, Foxl kept showing the old "not configured" state until you restarted it. Refresh re-reads provider credentials and model lists on the spot. - Chat: starter prompts on mobile actually work now. The tappable "try asking something like" rows under the mobile greeting had never been able to show anything; they do now, and your last greeting and prompts are remembered so they appear instantly on a cold start instead of a few seconds later. The desktop empty state is unchanged (greeting and composer, no prompt list). The composer no longer repeats the question already asked above it.
- Sidebar: switching product closes the mobile drawer, like every other destination in it, so you land on the screen you tapped instead of dismissing a panel first. The product switcher is also reachable by keyboard and screen reader for the first time.
- Foxl Code: AgentCore compute is now actually billed. A task costs two things - the model tokens and the machine it runs on - and the machine half was never charged on relay-routed tasks, because finalize treated the two as alternatives and kept only the larger. Both are now recorded, and a task's reported cost is the real total. Pre-flight cost estimates also no longer price a model at zero just because it was missing from an internal table.
- Foxl Code: budget settings that actually do something. The Budget section had four knobs, and two of them did nothing: an "Account-wide budget cap" that promised to reject new tasks (nothing enforced it) and a "Hard cap fraction" the server ignored. Both are gone, along with the per-task multiplier, leaving one number - max spend per task - which the relay really does enforce. The daily spend cap also could not be saved at its own default value; its limit now matches the server's.
- Foxl Code: Runtimes shows real live spend. The spend bar read $0.000 for the entire time a task was running, because that figure is only written when the task finishes. It now shows the running total the relay is metering. The fake mini-terminal preview, which could never display agent output, is gone.
- Foxl Notes: an AI summary can no longer land on the wrong note. If you clicked a different note while a summary was still being written, the finished summary replaced THAT note's contents and renamed it. The summary now always goes to the note it was started for, and it no longer overwrites whatever you happen to be reading.
- Foxl Notes: import a recording you already have. Drag a wav, mp3, m4a, or mp4 onto Notes - or use File > Import Recording... - and it becomes a new note with a full transcript, speaker labels, and the same AI summary, title, and export you get from a live recording. It runs through the exact same transcription path as recording, so an imported note behaves like any other note; the audio is kept too, so clicking a line still plays that moment.
- Foxl Code: one chat, like Foxl Agent. Sending the first message no longer jumps you to a separate task screen with what looked like a copy of your conversation. You stay in the same chat, the reply keeps streaming, and the new task appears in the left sidebar. Picking a task from the sidebar swaps that task's thread in place and reveals its status, plan, PR link, and live terminal above the same chat; picking nothing puts you back on a fresh chat. The composer no longer jumps from the middle of the screen to the bottom either - it is the same composer, so a half-typed message and its attachments survive your first send. The old "Clear chat" link is now "Reset session" in the task menu, next to Retry and Cancel.
- Chat: a marker when the model changes mid-conversation. Switching the model in the middle of a chat now drops a subtle "Switched to <model>" divider into the transcript, so the record shows which model answered which turns. It only appears on an actual change (consecutive same-model turns add nothing) and is saved with the conversation on app.foxl.ai and mobile, so it survives a reload or a second device. (On the desktop app the marker shows for the session; the divider is not yet persisted to the local chat database.)
Features
- Foxl Code: the loop closes itself. A batch of loop-engineering capabilities so a task drives from prompt to verified PR with less hand-holding:
- PR reviewer (maker-checker). When a coding agent opens a pull request, an independent reviewer agent fetches the diff, grades it against the original task, and posts a real GitHub review (approve or request changes with a structured assessment). On "request changes" the coding agent is automatically resumed with the feedback, up to two rounds.
- CI-aware self-heal. Foxl now ingests GitHub check/workflow results: a task shows a CI status chip, and a red run feeds the failing check back to the agent to fix instead of sitting silently.
- Verify gate. After a coding agent finishes, its repo's own test suite runs; a red suite blocks the PR and marks the task failed rather than "ready for review".
- Scheduled tasks. Set a task to run on a cadence (every N hours or a daily UTC time on chosen weekdays), from Settings or by asking the agent. Scheduled runs respect the same budget and beta gates as manual ones.
- Per-repo memory + custom instructions. Tasks start warm with a short brief distilled from prior work on the repo, plus account-level custom instructions and per-user MCP connectors.
- Cross-device chat history. Foxl Code chat threads are mirrored server-side, so a task's conversation is there when you reopen it on another device.
- Push notifications when a task is ready for review, merged, or fails.
- Foxl Code: more coding agents. Codex (default model GPT-5.6 Sol), Kiro, and OpenCode backends are enabled alongside Claude Code, and mid-run steering (read the agent's screen, send input, resume) now routes to each backend's own runtime instead of only Claude Code's.
Fixes
- Notes: transcript sentences no longer land out of order. With system audio + mic captured together (two transcription pipelines) or across the periodic socket rotation, a late-arriving sentence could be appended to the bottom of the live transcript - and stay there in the saved note - instead of slotting in where it was actually said. Segments are now kept in absolute audio order as they stream in, the transcript view sorts defensively, and previously saved notes are repaired on load (fixes copy, summaries, and send-to-notes built from those notes too).
Features
- Foxl Code: per-task conversations. Every task now has its own chat thread alongside the live terminal (Conversation | Terminal toggle in the task header, next to the status). The home composer starts a task and drops you into its thread; follow-ups there steer the same coding agent instead of starting a new one. Clear chat now clears only the thread you are in, and the autonomous heartbeat's updates get their own activity feed on the Tasks home page.
- Foxl Code: one chat UI everywhere. The Tasks home and a task thread now render the exact same chat surface (the glass-composer, rich reasoning + tool-call timeline). Previously the two used different code and looked unrelated. Each task also has its own address (/code-tasks/:id, like the Agent's /chat/:id); /code-tasks is the "new chat" home.
- Foxl Code: the "Tasks" title is New Chat. Clicking the Tasks title in the Code header (from anywhere, including inside a task) returns to the empty composer home and starts a fresh chat, mirroring the Agent header.
- One Settings for all three products (desktop). Opening Settings from Notes or Code now lands in the same two-pane Settings view the Agent product uses, drilled to that product's section - instead of each product showing its own standalone settings page. Every setting is reachable from every product, and the mobile sheet already worked this way.
Fixes
- Code Settings opens again. After viewing any task, clicking Settings in the Code sidebar footer kept showing the task instead of Settings: the task-selection sync replayed the remembered task over the fresh settings route on every remount. The sync now only drives task routes, and the desktop Settings entry renders the unified Settings directly.
- Code Settings coding-agent cards read properly. Inside the shared Settings column the coding-assistant picker used a 3-column grid meant for the wide standalone shell, so each card squeezed to ~200px and every label wrapped one word per line. The cards are now full-width rows, and product settings (Notes/Code) get a wider column than the plain preferences list.
- Code "New task" button works from anywhere. The in-shell New task button did nothing while a task detail page was open (only the Tasks page listened for its event); it now clears the selection and opens the composer from any Code page.
- One language setting for the whole app. The Code sidebar could show Korean labels while the rest of the app was English: its nav labels were missing from the shared locale files, and Notes kept its own UI-language picker in its own store. The language picker in Settings > General is now the single source of truth - it drives Agent, Notes (including the HUD window and AI output language), and Code together, and the Code nav labels are translated in all 10 locales.
- Foxl Code task view polish. A spawned task's prompt now shows as a proper user message bubble (it used to render as a washed-out grey box); the task header is a single tidy row; and a stray vertical scrollbar that appeared even on short pages is gone.
- Foxl Code: "Tasks" title now actually leaves a task. Clicking Tasks while viewing a task changed the address bar but left the task on screen; it now returns to the composer home.
- iOS: the top and bottom safe-area strips match the app. In dark mode the strips around the clock and home indicator showed pure black against the app's slightly-grey background (and could even show the opposite colour when the phone's system appearance differed); they now paint the same colour as the app in both themes.
Marketing
- The Foxl Code landing hero is now a live recording. foxl.ai/code plays a real screen capture of app.foxl.ai/code (typing a task, browsing the Task Plan) in a browser frame - retina-crisp, autoplaying muted loop with a play/pause control, a scroll scale-up entrance, and a poster fallback that respects reduced-motion preferences.
Features
- The mobile terminal has a reconnect button. Next to the new-tab chip, the refresh chip does what the desktop terminal's refresh does: tears the session's connection down and reconnects, re-hydrating the scrollback.
Design
- Glass everywhere it floats. The chat's scroll-to-bottom arrow is now a frosted glass chip like the header buttons, and the mobile terminal's bottom bar matches: quick keys and arrows as translucent glass pills on the dark surface, and the command box restyled as a frosted glass card with the same specular rim and lift as the message bar.
Fixes
- Spawning a terminal from the web app no longer shows two tabs. Starting a terminal on app.foxl.ai created one real session but two tabs (both looking selected): the desktop's "terminal appeared" push and the spawn request's own response both added the same session. The session list now merges by id, so one session is always one tab.
- Rolled back native iOS glass. v0.4.18's native Liquid Glass darkened the top and bottom of the app on real devices and the glass never showed: an iOS app is fullscreen, so unlike a transparent macOS window there is nothing behind the web view for glass to sample - making the web view transparent only exposed the app's black container. The header chips and message bar are back on the proven CSS glass everywhere.
- The mobile terminal now covers the whole screen. It slid in under the header (sidebar/title/menu chips stayed visible) because the animated app shell turns
position: fixedinto shell-relative positioning; the terminal page now renders outside the shell, so it truly covers everything, header included.
Fixes
- Desktop terminals appear the moment the app opens. On web and iOS the terminal list was fetched once at launch - before the desktop connection was up - so it came back empty and only a terminal opened *after* that point would show. The list now re-syncs as soon as the desktop comes online and on every app resume, so terminals already open on the desktop are there immediately.
Design
- Real Liquid Glass on iOS 26+. The header chips and the message bar now render actual system glass - native UIGlassEffect views tracked behind the web content via our new @foxl/capacitor-liquid-glass plugin (the electron-liquid-glass architecture, adapted to Capacitor) - instead of a CSS imitation. Older iOS, Android, and the web keep the CSS glass.
- Cmd+T shows and hides the terminal. One shortcut flips the chat's terminal view open and closed, from anywhere in the app (desktop menu accelerator + in-page shortcut on the web).
- The mobile terminal is a full page now, not a bottom split. Tapping the terminal in the message bar pushes a full-screen terminal in from the right (the iOS navigation push), so the PTY and its command input get the whole screen instead of fighting the chat composer, quick keys, and keyboard in one cramped stack. Swipe right from the left edge - or tap the round back chip - to return to the chat. Desktop keeps the bottom panel.
- Mobile header, reworked. The three round header buttons grew to a full 44px touch size, and search + new chat merged into one menu button: tap it and an iOS-style menu drops out of the corner (with the springy water-drop spread) holding Find in this chat, Search conversations, and New chat.
- Mobile sheets match the shell's roundness. The Settings sheet and the model selector sheet now use the same 28px corners as the main screen card when the sidebar drawer is open, so all lifted surfaces read as one family.
Features
- GPT-5.6 Luna joins the free tier. Free accounts can now chat with GPT-5.6 Luna (the fast, light GPT-5.6 tier) alongside Haiku 4.5, Sonnet 4.6, and Sonnet 5. Sol and Terra remain Pro+.
- Locked models sink to the bottom of the model picker. Models your plan can't use no longer sit greyed-out between usable ones - they collect in a "Requires upgrade" group at the end of the list.
Fixes
- Stop responds instantly on mobile. Two bugs stacked up: the web app's tunnel routing dropped the abort signal, so tapping Stop cancelled nothing - the reply kept streaming until the desktop finished; and on iOS the tap itself could be swallowed when it coincided with the keyboard dismissing. Stop now aborts on touch-down (with a haptic tick) and the abort reaches the stream and the desktop agent immediately.
- A failed reply now always answers back. Credits exhausted, a model not on your plan, permission or rate-limit errors used to vanish - no toast, no banner, no bubble. Every failed turn now ends with a clear in-conversation reply (a calm red-tinted message that persists in history) explaining what happened and what to do next, on desktop, web, and mobile alike.
- Tool activity chips stopped swapping identities for good. The tool name now rides the authoritative SDK event (not a lookup that could miss), and every pairing path - live stream, tunnel, and mid-stream reconnect replay - refuses to attach a result to a different tool's chip. Shell commands no longer render as "file read" or "web search".
- Model and provider names no longer clip in Settings on phones. Wide pickers (Notes AI model, translate model/language, save format) drop to their own full-width row on small screens instead of squeezing into an ellipsis next to the label.
Design
- Selects match the app's roundness. Dropdown triggers, menus, and rows across Settings now use the same softly-rounded corners as the chat model selector (rounded-lg triggers, rounded-xl menus) instead of the sharper stock corners.
Fixes
- CRITICAL: replies work again on mobile and app.foxl.ai without the desktop connected. Since v0.4.13, chats on the relay-only path rode a relay-internal
conversationIdfield (for chat-complete push deep links) that the relay forwarded verbatim to the model provider. Bedrock rejects unknown fields ("Extra inputs are not permitted"), so every model returned an empty reply with no message bubble. The relay now strips relay-internal metadata before any upstream forward, with a regression test that sends exactly what the apps send. - Tool activity chips no longer mislabel each other's work. When results arrived for interleaved tools (for example a web fetch and a shell command running in the same turn), a result that missed its id match was stapled onto whichever chip was last running - showing "Web search completed" over a python command, duplicated across rows. Results now carry the tool name and only ever pair with a chip of the same tool.
- Mobile header buttons line up with the message bar. The phone header's glass chips sat on a slightly narrower margin (px-3) than the composer strip (px-4), so the top row looked subtly off against the message bar. Both now share the same horizontal edge.
- Workspace is friendlier to touch. File and folder rows get taller tap targets on touch devices, the mobile back button is a proper 36px target, Save grows to a touch-sized button, and the editor uses 16px text on phones so iOS no longer zooms in when you tap it.
- Every recent blog post has its own cover. Four posts (v0.4.10, v0.4.12, v0.4.15, and the AirPods deep-dive) shared the v0.4.7 sessions thumbnail; each now has a cover drawn from its own story.
Fixes
- Skills, Channels, and custom tools (MCP) are reachable again. When Integrations moved from the sidebar into Settings, its five-tab bar (Integrations / Skills / Channels / System / Custom) was left behind in the old page header - so Skills, Channels, and the System/Custom tool pages (including custom MCP servers) silently disappeared from the UI. Settings > Integrations now carries all five tabs inside the section as a segmented control, on desktop and mobile.
- The Integrations section scrolls on mobile. The Settings sheet expects each section to own its scrolling; Integrations didn't, so the section was frozen at the top on phones. The new hub owns its scroll container.
- Integrations dropped its gradient hero for a quiet prompt strip. The rotating full-color gradient carousel is gone; in its place a single flat row shows one featured integration with a real example prompt and a "Try in chat" button - tap the row to see the next example. Less visual noise, zero idle animation cost (the carousel was a steady compositor load on phones), and the page now reads like the rest of Settings.
- Usage scrolls (and loads fast) on the phone. The Usage section didn't own its scrolling inside the Settings sheet, so it was frozen at the top on mobile - fixed. Its dashboard also caches to durable storage now, so reopening the app paints the last numbers instantly and refreshes in the background instead of holding a spinner through a full tunnel round trip. The headline metric scales down on small screens instead of overflowing.
- Workspace opens instantly on the phone. The file tree now persists to durable storage: relaunching the app paints the last known tree immediately and refreshes in the background, instead of spending seconds on a spinner while the tree crosses the tunnel. (Demo data shown while the desktop is disconnected is never persisted.)
- Your name no longer gets stuck as "..." in the sidebar. Opening the app after being away could leave the account footer showing "..." even though you were signed in: the profile lookup sent the expired session token, got rejected, and never retried - on the web a manual refresh papered over it, but the app has no refresh gesture. The lookup now refreshes the token first, re-runs automatically when a refresh happens or the app returns to the foreground, and keeps showing the cached name instead of blanking to "..." while it revalidates.
- The Skills page dropped its gradient hero too. Same treatment as Integrations: the rotating gradient carousel is replaced by a flat prompt strip (tap to see the next example) - no ambient animation left anywhere in Settings.
- Usage Breakdown folds on the phone. The per-model chart in Account is collapsed behind its headline total on mobile - tap to expand. The number you check stays visible; the dozen bar rows no longer push everything else off screen.
- A tall message bar no longer covers the conversation. The chat's bottom scroll room was sized for the short one-line composer, so a multi-line draft or an attachment card sat on top of the last messages with no way to scroll them clear. The scroll area now tracks the composer's real height - as the bar grows, the conversation gains exactly that much extra room, and if you're reading the bottom it stays pinned above the bar.
- Attachment chips show their remove button on the phone. The X to remove a pasted-text block, image, or document from the message bar only appeared on hover - a concept touch screens don't have, so on mobile there was no visible way to remove an attachment. On touch devices the X is now always visible as a quiet gray chip (and slightly larger for the finger); desktop keeps the hover reveal, now in the same quiet gray instead of alarm red - removing a draft attachment is routine, not destructive.
Features
- Share from the iPhone app. Every chat reply now has a share button that opens the iOS share sheet (Messages, Mail, AirDrop, Notes...), and Notes exports (.md / .docx) go through the share sheet too - previously the browser-style download simply vanished on iOS, so exporting from the phone didn't work at all.
- The app icon shows an unread badge. When replies finish while you're away, the home-screen icon carries a count that matches the in-app unread state and clears as you read. Chat-finished pushes also set the badge, and opening the app sweeps its delivered notifications out of Notification Center instead of letting them pile up.
- Home-screen quick actions. Long-press the Foxl icon for New Chat and New Recording - New Recording jumps straight into Notes and starts recording.
- foxl:// links now navigate the iPhone app. Links like foxl://chat/{id} (the sidebar's "Copy link") and foxl://chat/new open the right conversation on the phone instead of just launching the app; they work from a cold start too.
- The status bar matches the app theme on iPhone. Switching Foxl between light and dark now flips the clock/battery icons to stay legible, instead of leaving them at whatever the system default was.
- Unread tracking works on the phone. A reply that finishes in a background conversation now marks it unread (sidebar dot + icon badge) on the mobile app, matching desktop behavior.
- Cmd+1 / Cmd+2 / Cmd+3 switch between Foxl Agent, Notes, and Code. The product switcher now has first-class keyboard shortcuts on desktop, listed in the View menu so they're discoverable like any native app.
- "New Recording" in the menu-bar tray. Right under New Chat: starts a Notes recording immediately and brings up only the floating meeting panel - the main window stays wherever it was, so you can capture a meeting without switching context.
- The floating meeting panel opens instantly. It used to build its window from scratch on every open (and closing destroyed it), which took visible seconds. The panel is now pre-warmed hidden at startup and hides instead of closing, so opening it - from the tray, from Notes, or automatically when a recording starts - is immediate.
- Bottom sheets on the phone now swipe down to close. The model picker and the Settings modal follow your finger 1:1 on a downward drag and dismiss past the threshold (or on a quick flick), springing back otherwise - the standard iOS sheet gesture the grab handle always implied. Scrolling inside a sheet still works: the drag only takes over when the content is at its top. Both sheets also float with a slim gutter on each side instead of running edge-to-edge. (Follow-up during staging: the gesture now attaches at the document level so the portal-mounted model sheet actually receives it, and a dismissed sheet slides straight off instead of replaying its exit animation from the top - the brief "bounce back up" glitch.)
- Key actions on the phone now give haptic feedback. Opening the sidebar, starting a new chat, opening Settings, sending a message, and picking a model or reasoning effort each give a light native tick (a firmer one for send). Phone app only - web and desktop are unchanged.
- Notes recordings keep going in the background on iPhone. Starting a recording and switching apps (or locking the screen) no longer kills it. iOS suspends web-view microphone capture the moment an app leaves the foreground, so the iPhone app now records through a native audio engine instead - paired with the audio background mode, the capture and live transcription continue with the system's recording indicator showing. Phone calls interrupt and resume cleanly, and switching to or from AirPods mid-recording re-anchors the capture instead of going silent.
Features
- Mobile notifications now have per-category controls. Settings > Notifications on the phone splits into individual toggles - chat replies, scheduled tasks, product updates, and promotions & tips - like a proper app, instead of one on/off. Each toggle is stored on the relay (the thing that actually sends the push), so a change takes effect immediately across your devices. Promotions are off by default and only ever sent to people who turn them on.
- "Open Settings" jumps straight to the system notification screen. When iOS or Android has notifications turned off for Foxl, the settings screen now has a button that opens the OS app-settings page directly - the standard way to re-enable them, since no in-app toggle can grant the system permission itself. A "System notification settings" row is always available too.
Fixes
- Chat-finished notifications now actually arrive on the phone. The chat-complete push was only wired into the desktop tunnel path, so a reply sent straight from the phone (or app.foxl.ai) finished silently with no notification when the app was backgrounded. The relay now fires the push on the mobile chat path too, and keeps the reply generating to completion in the background even after the app is backgrounded or locked. The iOS app also declares the remote-notification background mode so the system can wake it to deliver.
Features
- See which chats are still answering, right in the sidebar. Every conversation with a reply in progress now shows a small right-aligned spinner next to its name - whether that chat is open in front of you or generating in the background. Replies already keep running when you switch to another chat; now the sidebar makes that visible so you know at a glance what's still working. On desktop the spinner steps aside for the delete button on hover (same slot, no layout jump); on mobile it stays put.
- The phone has its own notification settings. Settings > Notifications on mobile is no longer the desktop's notification page: it now shows the phone's push controls - whether iOS has allowed notifications for Foxl (with a one-tap request, or a pointer to system Settings if previously denied), and per-kind toggles for chat-reply and scheduled-task pushes. The toggles are stored on the relay, which is what actually sends the push, so turning one off takes effect immediately for every registered device. Desktop notification settings are unchanged and stay on the desktop.
Fixes
- Notes recordings now capture the meeting's audio, not just your mic. Recording with headphones or AirPods produced an empty or one-sided transcript: the other participants' voices played inside your headphones and never reached the microphone, and system-audio capture only ran when live translation was armed. Every recording on desktop now captures system audio (the sound this Mac is playing) and mixes it with your microphone - both in the transcript and in the saved audio file. A new toggle in Settings > Recording turns the capture off. This needs macOS's "System Audio Recording Only" permission, which macOS never prompts for on its own - it just hands over a working-looking but silent capture - so Foxl now proves the capture end-to-end instead of trusting it: a new onboarding step and a test button in Settings > Recording play a short inaudible tone and verify it comes back through the capture, with a one-click "Open Settings" jump straight to the right privacy pane when it doesn't. The same self-test runs at the start of every recording; if the capture is dead, a standing warning appears in Settings until it's fixed. On macOS older than 14.2, where the system can't provide loopback audio at all, it tells you up front that the recording is microphone-only.
- A background chat's tool activity no longer bleeds into the chat you're reading. When a reply that uses tools kept running in one conversation and you opened another, the first chat's "running tool" chips and status could appear at the bottom of the new chat - pure display bleed, nothing was actually shared. Tool status is now tracked per conversation on every path (desktop stream, WebSocket events, relay web), and a background turn can no longer paint its bubbles or tool chips into whichever chat is on screen.
- A single
~no longer strikes through your text. Chat and Notes render Markdown, and a lone tilde used as a range ("rank 0~7, rank 8~15") was being paired with the next tilde and struck through everything between them - so a perfectly normal sentence showed up with a line through it. Strikethrough now requires the standard double tilde (~~like this~~); single tildes are left as plain text. - The keyboard animation no longer reflows the app every frame. Docking the composer on the rising keyboard animated the shell's height, and height is a layout property - every frame of the 250ms transition forced a full relayout of the app, which stayed visibly choppy no matter how much paint work was removed. The composer now rides the keyboard on a compositor-only transform and the real layout change commits exactly once, at the moment the keyboard settles - the standard technique for smooth keyboard docking in embedded web views.
Fixes
- The keyboard animation is smooth on mobile. While the app shrank to dock the composer on the rising keyboard, every animation frame re-rendered the glass surfaces' backdrop blur - the most expensive per-frame job in the iOS web view, and the source of the stuttery keyboard feel. During the 250ms docking transition the glass now switches to a near-opaque matte and the frost returns the moment it settles; the swap is imperceptible, the dropped frames are gone.
- Sidebar text no longer peeks out while the keyboard opens. The reveal drawer sits at full screen height behind the app shell, so when the shell shrank for the keyboard, drawer rows ("For You") showed through the strip between the shell and the keyboard. The closed drawer is now fully hidden, with the hide delayed past the close animation so it never vanishes mid-slide.
- Glass looks closer to native iOS material on phones. Touch devices with backdrop-filter support now get a more translucent tint with stronger blur and saturation on the header buttons and composer - nearer the real iOS 26 material in the native app - while desktop keeps its subtler treatment.
- The content panel border is back to its v0.4.1 weight. The recent full-opacity border on the desktop inset shell read too heavy; restored the original softer hairline.
Features
- Get a notification when your chat finishes on mobile. Send a message from the phone, switch apps or lock the screen, and Foxl now delivers a system push when the response is ready - tapping it opens that conversation. The push only fires when the app actually stopped watching the stream mid-turn; foreground chats never notify.
- The product switcher is now three tabs. The sidebar's "Foxl Agent" dropdown is replaced by a segmented Agent / Notes / Code control on both desktop and mobile - one tap to switch, and the active product is always visible.
- Swipe to manage chats on mobile. Conversation rows in the mobile sidebar now swipe left to reveal Rename and Delete, replacing the long-press menu. The drag tracks your finger and settles with a spring; tapping an open row closes it. Desktop keeps the right-click menu and hover actions.
- Settings is a native mobile sheet, and all three products now share it. On the phone, Settings slides up as a glass sheet over the app (the sidebar stays put beneath it, iOS-style) with a single round back button - no title bar, no divider - and sections push in from the right and pop back like a native settings app. Your Account, Integrations, Mobile/Web, and Usage all moved inside it, and Foxl Notes and Foxl Code settings are sections here too, so everything lives in one place. Tapping your avatar on mobile opens Settings straight to Account. Each section loads on demand, so the sheet opens instantly. Desktop keeps its familiar two-pane Settings page unchanged.
Fixes
- A dead desktop connection no longer causes a burst of repeated Gateway Timeouts. When the desktop's tunnel socket died silently (sleep/wake, network switch - no close event ever arrives), the relay kept writing every request into the dead socket for up to two minutes: each one waited its full 10 seconds and failed with a 504 while the dashboard still showed green. A live desktop always answers within 8 seconds, so the relay now treats one full request timeout as proof the socket is dead: it closes it and flips the desktop offline immediately. The desktop redials within about a second, so the next request goes through instead of joining the timeout storm.
- The chat composer now rides the keyboard smoothly on mobile. Tapping the input used to stutter as the composer rose: the layout transition ran on a different curve than the iOS keyboard, and every animation frame fired scroll events that re-rendered the chat mid-animation. The shell now animates on the keyboard's own timing curve and scroll-state updates pause until the keyboard settles, then reconcile once.
- Mobile chat is redesigned to mobile norms. The empty state drops the centered logo for a large left-aligned greeting, and suggestions become quiet full-width rows instead of centered pill buttons. Message text is 15px with relaxed line height (was 14px), the input is 16px (which also stops iOS from zooming the page on focus), sidebar rows grow to 40px touch targets with 14px labels, composer buttons grow to 36px, the disclaimer line under the composer is hidden on phones, and tap-highlight flashes are gone.
- The mobile chat header follows the round-button pattern. The hamburger and the header actions are now round soft-filled buttons (like the Claude and ChatGPT apps), the header grows to a comfortable 48px, and the two search buttons collapse into one on phones - the conversation search palette covers both finding a chat and finding within it.
- The message composer floats on a soft shadow. On desktop and mobile the input card now carries a subtle diffuse elevation that deepens slightly while typing, instead of sitting flat on the page. The border is thinner and the edge definition comes from a tight shadow fade hugging the card.
- Free users with fresh monthly credits can chat again. One oversized agent turn could overdraw the internal balance by more than a month's grant; that debt silently carried across months, so every later request failed with "Credits exhausted" while the Account page still showed 10 credits available. Monthly free credits are use-it-or-lose-it, so the refill now also clears any carried overdraft, and affected accounts were reset.
- Chat errors are visible again. A regression dead-wired the persistent error banner, so a failed send (out of credits, plan-gated model, relay errors) produced no feedback at all - the reply just never came. The banner is rewired: credits/plan issues show the amber upgrade prompt, other errors show the alert with the real message.
- The chat header is now translucent glass. Content scrolls beneath a blurred, semi-transparent header (same treatment as the foxl.ai site), and tapping the Foxl logo in the sidebar returns home (new chat / Notes / Code tasks) on desktop and mobile.
- Scrolling the chat while touching a message bubble works again. A containment rule applied to every scrollable element stopped touch gestures from reaching the transcript when they started on a nested scroller (code panes, text blocks); containment is now scoped to the top-level scrollers.
- A request that lands exactly on a dead connection recovers by itself. The relay heals a silently-dead desktop link the moment one request times out, but that first probe request still surfaced as a Gateway Timeout in the app. Read requests now retry once after the desktop's ~1s redial, so the healing is invisible instead of an error flash.
- Fixed an accessibility violation when the sidebar opened over a focused chat input on mobile (focus retained under aria-hidden). The composer no longer steals focus on phones - the keyboard appears when you tap the input, not when the app decides.
- Vertical scrolling no longer freezes intermittently on mobile. A legacy iOS scrolling hint forced the chat list onto a deprecated scroll path where streaming content could stall it until the next touch; removed.
- Dropdowns inside the mobile Settings sheet open again. Selects, popovers, and menus were rendering behind the sheet (a stacking-order regression), so tapping the language picker or any Settings dropdown did nothing. They now layer above the sheet.
- The chat follows a long answer as it streams again. When a single reply or thinking block grew tall, the view stopped scrolling with it - you had to keep tapping "scroll to bottom." Auto-scroll now tracks the content as it grows (not just when a new message arrives), and still hands control back the moment you scroll up, exactly like before. This restores the v0.3.x stick-to-bottom behavior.
- The message bar has a crisp, defined edge again. The glass composer's border was washed out by the blur and nearly black in dark mode; it now has a clear hairline (like Codex), a soft gray card in dark mode instead of near-black, and a symmetric shadow that lifts the top and bottom edges evenly instead of pooling at the bottom. Foxl Code's composer matches. The inset content panel and the mobile sidebar drawer also get a defined border.
- Removed the "AI can make mistakes" line under the composer in both Foxl Agent and Foxl Code - it added a row of clutter on every screen.
Fixes
- Remote access is stable again: no more connecting-then-dropping, and chat is fast. Three tunnel regressions from v0.4.7's connection-fencing rework are fixed, restoring the v0.3.x behavior.
- A phone or browser no longer multiplies into many device rows on the dashboard. The relay was dropping the client fingerprint when routing a web connection to its per-user hub, so every reconnect from the same device looked brand-new and piled up a fresh connection on every network blip, token refresh, or app resume. The fingerprint is now forwarded and the hub keeps exactly one live socket per client, closing the stale one.
- A desktop reconnect no longer drops in-flight requests. A reconnect (sleep/wake, network change) used to abort every request in flight with a "connection was replaced" error; because the web client also cycled its status socket on every token refresh, this fired constantly and looked like "connected for about ten seconds, then everything drops and chat gets slow." In-flight requests now survive a reconnect and complete from the original reply.
- The web app stops needlessly cycling its status connection. It used to tear down and reopen the status WebSocket on every token refresh and app resume even when the socket was healthy; an open tunnel socket stays valid across a refresh, so it now stays put and only reconnects when it is actually down.
- The desktop no longer shows two or three "Web client connected" pop-ups for a single connection. A phone or browser opens a few tunnel sockets at once (presence, chat, terminal sync) and each one, plus the chat socket's own end-to-end handshake, fired its own notification. The desktop now coalesces them into one notification (and one in-app toast) per device.
Fixes
- Mobile/web tunnel requests no longer hang for 10 seconds and then fail with a Gateway Timeout. When the desktop's machine ran out of local network ports (a socket-exhaustion storm that also breaks other apps), the desktop's loopback call to its own server could stall with no error and never reply, so the relay waited out its full request timeout and returned a 504 while the desktop still showed as connected. The desktop now bounds that local call and returns a real error instead of silence, and the relay always matches a desktop reply to its pending request - even if the desktop reconnected while the request was in flight - so a live desktop stops surfacing phantom timeouts.
Features
- Sessions on the Account page now reflect real devices. Every Foxl installation and browser profile mints a stable client identity, so the session list groups by actual device instead of by browser version string. Old duplicate rows (the same Safari appearing many times) collapse into one entry, and revoking a session signs out exactly that installation.
- Per-provider usage breakdown on the Account page. The 30-day usage table now separates the same model served by different providers (AWS Bedrock vs OpenAI OAuth), shows month-to-date credits from the immutable usage ledger, and prices them consistently at the credit rate.
Fixes
- Foxl Desktop now starts without internet. When the machine was offline (DNS unresolvable), macOS proxy auto-discovery failures made even the local
127.0.0.1app page fail withERR_ADDRESS_INVALID, so the app never got past the startup screen. Loopback now bypasses any system proxy, and subframe or embed failures no longer knock a healthy app page back to the startup splash - which also removes the rapid splash/app flashing loop seen while offline. - The startup screen matches the app's light theme. The splash was a dark-only page with an inverted monochrome logo; it now uses the light palette and the brand-color logo, so launching no longer flashes a black screen on light-mode systems.
- Signing in on two app surfaces at once no longer logs you out. When the renderer and the Electron shell refreshed the same session within a few seconds of each other, the relay treated the second refresh as token theft and revoked the whole session family. Concurrent refreshes are now idempotent: both callers get the same replacement token, while replays outside the short concurrency window still revoke the family.
- A reconnecting desktop can no longer be marked offline by its own old connection. Tunnel connections are now fenced by a connection id, so a delayed close event from a previous WebSocket cannot flip the replacement connection to offline. Wake-from-sleep reconnects collapse into a single jittered backoff owned by the tunnel client instead of three racing timers.
- Mobile/web tunnel status is push-first. The always-on HTTP status polling loop is gone; status arrives over the push channel and HTTP is only used as a bounded recovery probe, which also cuts steady-state relay reads.
- Credit balances update on activity instead of polling every 15 seconds. The sidebar and Account balances refresh when a chat turn completes, when the app resumes, or on manual refresh, removing a constant background request loop on every open Foxl surface.
- Model favorites are provider-aware. Starring a model on one provider no longer stars the same model id on every provider; existing favorites are migrated in place.
- Foxl Code's collapsed desktop sidebar now keeps long task histories contained. Pressing Command-B no longer expands every task avatar down the icon rail and pushes the remaining navigation away. The task section now matches code.foxl.ai with a bounded, independently scrolling list and a clipped bottom edge that indicates more tasks.
Fixes
- GPT tool runs no longer stop with an empty answer. Bedrock can report a terminal Responses API failure after a long tool run. Strands 1.9 ignored that event and finalized the turn as a successful but empty response. Foxl now turns it into a model error so transient failures can retry and final failures appear through the same error toast used by other models.
- Bedrock GPT context limits now match the actual 272K window. GPT-5.6 Sol/Terra/Luna, GPT-5.5, and GPT-5.4 were shown and configured as 1M-context models even though Amazon Bedrock Mantle rejects prompts above 278,528 tokens. The AWS and Foxl relay paths now advertise and configure the real limit, and the chat context meter uses it. Direct OpenAI and ChatGPT OAuth keep their 1M windows.
Features
- Launch at login. Foxl now starts automatically when you sign in to your computer, so your 24/7 agent is always running. Enabled by default; toggle it off under Settings -> About.
Fixes
- Offline behaves like a normal desktop app now. Losing internet used to flash a retrying error panel on and off (the reconnect ticker strobed the overlay). The connection-lost screen was redesigned into one calm panel that distinguishes "you're offline" (just waits and auto-reconnects, no blinking) from "the local agent server stopped" (offers Restart), and both edges are debounced so a flaky network can't make it flicker. Sending a chat while offline now shows a plain "You're offline" message instead of a raw "Failed to fetch", and the app recovers on its own when the network returns.
- GPT-5.6 on AWS: a transient hiccup mid-conversation no longer makes the agent quit tool-calling or go silent. GPT-5.6 (Sol/Terra/Luna) runs on the Amazon Bedrock Mantle OpenAI Responses API, which surfaces transient server errors (503/500/502) in the OpenAI SDK's error shape (a plain
status), not the AWS shape our retry logic recognized ($metadata). So a brief Bedrock blip during a long GPT-5.6 tool loop was treated as fatal instead of retried, and the turn stopped mid-way or returned nothing. The retry policy now recognizes both error shapes (plus connection/timeout errors), so these blips are retried with backoff on every GPT-5.6 path. Verified end-to-end against real Bedrock: single and multi-round tool loops complete cleanly. - Favoriting a model no longer shows it (and a lit star) twice. A favorited model was rendered both in the pinned Favorites group and again in its provider group, so a single favorite looked like two selected stars. Favorited models now appear only in the Favorites group.
- GPT-5.6 Sol, Terra, and Luna now read distinctly in the model picker. All three tiers showed the same "Advanced reasoning" subtitle; they now describe their actual role - Sol for flagship reasoning, Terra for balanced performance at half the cost, Luna for fast, cost-efficient inference. The Claude model subtitles were also refreshed to match Anthropic's current descriptions.
- No more "Tool 'workspace_memory_save' not found in registry". The system prompt always told the model to use the workspace-memory tools, but some setups (Claude Code subscription provider, or memory tools disabled in Settings) run without them - so the model called a tool that wasn't there and the turn errored. The prompt is now generated from the tools actually available for the turn: it only mentions memory, subagents, exec, and scheduling when those tools are present, on every agent path (chat, subagents, channels, scheduled runs, heartbeat).
- Foxl Code: per-task budget is a hard cap under concurrency. A coding task's budget was checked by reading spend that is only written after a response finishes, so several requests running at once could all pass the check and blow past the budget (and the account's credits). Each request now atomically reserves its worst-case cost before the model runs and settles to the real cost afterward, so parallel requests can't collectively exceed the cap.
Changes
- Upgraded the Strands Agent SDK to v1.9.0 (from 1.7.0 on desktop, 1.5.0 on the Forge orchestrator). Picks up the fix for tool-use metadata in streamed
contentBlockDeltaon non-standard models (GPT-5.x / GLM / Kimi routed through the SDK), tool usage after reasoning content, and durable message identifiers. No API changes on our side - all the SDK symbols Foxl uses are unchanged, and the one breaking rename (LocalMemoryStore -> TestMemoryStore) is a store we don't use.
Features
- OpenAI GPT-5.6 Sol, Terra, and Luna - day-one support. OpenAI's newest model family (GA on Amazon Bedrock today) is available across Foxl: the chat model picker on desktop / app.foxl.ai / Foxl Notes, and as Codex coding-agent models in Foxl Code. Sol is the flagship reasoner ($5/$30 per 1M tokens), Terra the everyday workhorse ($2.50/$15), Luna the fast, affordable tier ($1/$6) - Pro plans and up, priced at OpenAI first-party rates. All three support reasoning effort up to the new
maxlevel. Available two ways: on your Foxl credits via Amazon Bedrock, or on your own ChatGPT Plus/Pro subscription through the OpenAI (OAuth) provider - no API key needed. - Accurate prompt-cache billing for GPT models. GPT-5.6 introduces billed cache writes (1.25x input) and discounted cache reads (90% off); the relay meter now decomposes OpenAI's combined input-token count into plain input, cache reads, and cache writes, so cached GPT traffic is billed at the discounted rates instead of full input price.
Fixes
- Chat no longer fails with "Invalid or expired token" while the sidebar still shows you signed in. The relay access token expires every 12 hours; the sidebar and account surfaces silently refresh it, but sending a chat message hit the relay with the stale token and treated the 401 as a dead session - wiping both tokens instead of refreshing. The message path now refreshes the token (sharing the same in-flight refresh as the rest of the app) and retries the send once; you only get signed out when the refresh token itself is no longer valid.
- AWS provider: GPT-5.6 listed once and actually works. The desktop AWS provider briefly listed each GPT-5.6 tier twice (once under Bedrock, once under OpenAI-on-Bedrock); the Bedrock-routed copy failed with "The provided model identifier is invalid" because these models are served only by the Mantle Responses endpoint. The provider split is now derived from the catalog (any
openai.*Bedrock id is Mantle-only), and a stale saved selection self-heals onto the right route instead of erroring. - Images stay part of the conversation. Attached images used to reach the model only in the turn you sent them; after the app restarted (or the agent was rebuilt), later turns saw just a file-path hint - so asking "read that image again" got "I don't see an image". Following the standard ChatGPT/Claude conversation model, image attachments are now part of the durable conversation record and are rehydrated into real vision blocks when history is restored (newest 10 images; older ones fall back to the path hint), so the model can keep referring to what you showed it.
- Pasted screenshots stay readable. Chat image attachments over the API size limit were converted to JPEG with quality walked down as far as 30% (and dimensions halved as a last resort), which smeared screenshot text - the model would sometimes answer that the image resolution was too low. Oversized images now try lossless PNG first, then WebP, with a gentler quality floor and a 0.75x (not 0.5x) fallback downscale; the raw size cap is also corrected to fit the API's 5MB base64 limit (a ~4MB paste used to slip through unmodified and get rejected).
- Signing in from Foxl Notes no longer lands you in Foxl Chat. The desktop deep-link login (and one web OAuth fallback path) hardcoded the post-login destination to Chat, so the sidebar said Notes while the content pane showed Chat. Every sign-in path now returns to the product you were in - Notes stays Notes, Code stays Code - and history navigation keeps the sidebar product and the content pane in lockstep.
Fixes
- Foxl Notes: duplicate transcriptions are structurally impossible now. Live-translate mode used to run TWO parallel transcription streams (your microphone + the meeting's system audio); on speaker setups the mic re-heard the remote party, so the same sentence landed twice - and the text-similarity filter that tried to catch this was unreliable. The two captures are now MIXED into a single stream before transcription, so every utterance reaches the transcriber exactly once. This also removes the phantom "Remote N" speaker label space, and the saved audio recording now includes the remote party (it was mic-only before).
- Foxl Notes: fewer phantom speakers. The speaker label for a line is now the majority vote across the line's words instead of the first word's label, so a single misattributed word no longer spawns a new "Speaker N"; single-stream capture also halves the label spaces (no more parallel Speaker/Remote numbering for the same voices).
- Foxl Notes: in-progress (italic) lines are readable again - they keep the italic style but drop the hard-to-read faint gray in both the transcript panel and the live glass panel.
Fixes
- Foxl Notes: no more duplicate transcriptions. On speaker (non-headphone) setups the microphone re-heard the remote party, so the same utterance could appear twice - once as "Remote N" (system audio) and once as "Speaker N" (mic). The mic now captures with echo cancellation, and a cross-feed duplicate filter drops the echo copy when both still slip through.
- Foxl Notes: the live panel accents are back to the classic yellow (the blue accent experiment is reverted) in both dark and light themes.
- Foxl Notes: the live panel's Notes tab is the real editor. Same rich editor as the main window - toolbar, headings, lists, task lists, tables, code blocks, undo/redo - instead of a plain-text box, with edits syncing live in both directions.
- Foxl Notes: "Open saved file" in the note's right-click menu opens the auto-saved Word/Markdown export directly (or the save folder if the file isn't there yet).
- Foxl Notes: full editor toolbar everywhere. The live panel's Notes tab and the mobile editor now carry the complete formatting toolbar (headings, emphasis, lists, quotes, code, tables, images, alignment) scrolling horizontally, instead of an 8-button subset; the panel toolbar wears the glass styling.
- Foxl Notes: better live translations. Once a sentence finishes, it is re-translated with the surrounding dialogue as context and the rough in-progress translation is replaced in one shot; the "translating..." placeholder is gone - translations simply appear as they stream.
- Foxl Notes: honest sign-in messaging. Recording and transcription need a (free) Foxl account; the empty screen, mobile home, and onboarding now say so up front - the onboarding sign-in step carries a "Required for recording" badge and the start button reads "Sign in" when signed out instead of a Start Recording button that silently did nothing.
Features
- Foxl Notes: click a transcript line to hear it. Recordings now keep their audio on disk (desktop), and clicking any line in a saved note's transcript plays the recording from that exact moment - the line under the playhead stays highlighted, and clicking it again pauses. Timestamps stay accurate across long recordings (they previously reset every few minutes internally), and deleting a note removes its audio file too.
- Foxl Notes: light and dark glass panel. The floating meeting panel now follows the app theme automatically, with an override (Auto / Dark / Light) in its settings menu. Accents moved from yellow to Foxl blue.
- Foxl Notes: mini mode is a real caption strip. No recording dot or timer - just the latest lines, with the visible row count adapting to however tall you drag the window (newest bright at the bottom, older lines dimmed). Your mini size is remembered. The expand button stays visible.
Fixes
- Foxl Notes: clear error when credits run out. Starting (or continuing) a recording with exhausted credits now shows the actual reason instead of silently doing nothing; a mid-recording denial stops the recording with a message.
- Foxl Notes: one unified sign-in. Signing in from Notes (including onboarding) now returns to this app via foxl:// - it previously deep-linked to the retired standalone Notes app (foxlnotes://), which could open the wrong window and land you on Chat.
- Foxl Notes: transcript playback highlights exactly one line - previously the last line of every earlier speaker group lit up too.
- Foxl Notes: settings dropdown no longer clips its values at the panel edge.
Features
- Foxl Notes: floating meeting HUD (glass panel). Starting a recording on the desktop now opens a compact always-on-top companion window with the live transcript, translations, timer, and stop/pause controls - so you can follow the meeting while other apps are full screen. On macOS 26+ it renders with the native liquid-glass material. Toggle in Notes Settings > Recording.
- Foxl Notes: meeting coaching report. A new Report tab analyzes your meeting after it ends: overall score, speaking share and pace, a skill breakdown (language use / fluency / grammar), sentence-level feedback with suggested phrasing, action items, and expressions to learn. Turn on "Report for every meeting" in Settings to generate one automatically after each recording.
- Foxl Notes: sample report on first run. New users see a complete sample meeting note with a finished coaching report before their first recording.
- Foxl Notes: first-run onboarding. Opening Notes for the first time now walks through language, sign-in, and microphone setup; replay it any time from Settings > Show guide.
- Google profile photo as your default avatar. Signing in with Google now uses your Google profile picture across Foxl and Foxl Notes; you can replace or reset it from the Account page.
Improvements
- Foxl Notes: live translation now translates complete sentences. Instead of translating each speech fragment as it arrived (choppy half-sentence output), the transcriber's fragments are stitched into full sentences before translation, with a short flush timeout so trailing fragments still arrive.
- Foxl Notes: turning on live translation picks a sensible target language automatically (your UI language) instead of staying inert until a second setting was found and set.
- Foxl Notes: Settings reorganized into clear sections - Recording and transcription, AI features, Live translation, Auto-save and export, General, About and updates - matching the Foxl Agent settings layout.
Fixes
- Foxl Notes: the Start button works with live translation on. Recording with live translation captures the other party's system audio; the desktop app was missing the screen/audio capture handler, so the presigned capture request failed instantly and the Start button appeared dead. Both dev and production Electron builds now register the handler (with loopback system audio on macOS/Windows).
- Foxl Notes: recording no longer fails with "Failed to execute 'start' on 'MediaRecorder'". System-audio (live translation) and some virtual audio devices reject the local audio-copy recorder; that copy is optional (it was never saved to disk), so the error is now non-fatal and transcription proceeds normally.
- Foxl Notes: exports now default to a "Foxl Notes" folder inside Documents instead of scattering files in the Documents root. Still configurable in Settings > Auto-save location (an existing custom folder choice is respected).
- Foxl Notes: HUD transparency slider in Settings > Recording controls how see-through the floating meeting panel is, updating live while it is open.
- Foxl Notes: the live panel grew into a full meeting companion. Live / Notes / Background tabs (notes are editable right in the panel, markdown supported), an AI chat with message bubbles and one-tap reply suggestions, a hamburger menu (transparency, fully-transparent ghost mode, text size, live translation, suggestions, always-on-top), and a mini mode that shrinks the panel to a tiny caption pill for screen sharing. The panel stays open after Stop so it works across back-to-back meetings; auto-open on record start is a setting. Live captions auto-scroll but pause when you scroll up (jump back with the Latest button).
- Foxl Notes: translation keeps up with continuous speech. In-progress (italic) captions now translate in rolling chunks instead of waiting for the sentence to finish; Transcribe partial-results stabilization makes finals land in smaller, faster units; filler-only fragments ("I", "um") are dropped instead of translated; and the translator never replies with meta commentary ("I cannot translate...") anymore.
- Foxl Notes: personal AI context in Settings > AI features - tell the assistant who you are and what your meetings are about, and HUD answers and reply suggestions take it into account.
- Foxl Notes: auto title works independently of auto summary, and auto-saved files are named after the generated title (never "New Transcript"), prefixed with the date:
2026-07-10 - Title.docx. - Foxl Notes: speakers are diarized again ("Speaker 1/2", system audio as "Remote 1/2") instead of everything being labeled ME.
Features
- Find any past conversation from the agent. A new conversation search lets the assistant look back through your earlier chats by keyword (full-text search), so it can recall what was actually discussed instead of only what was saved to memory. The chat search box is faster and more accurate too.
- Foxl Notes: live translation. Turn on live translation in Notes settings, pick a target language, and each incoming line of the other party's speech is translated on the spot as the meeting happens - built for following meetings in a language that is not your first. Uses a fast model by default.
Improvements
- Sidebar shows the current chat's title in the header instead of a generic "Chat" label.
- Scroll date overlay on the chat and Notes lists shows the exact date of each session (Today / Yesterday, then the real date) as you scroll, so it is clear which conversation happened when.
- Notes controls pack cleanly in small windows - buttons collapse to icons and an overflow menu instead of overlapping.
Fixes
- Foxl Notes no longer crashes to an error screen when switching between Notes and Chat while a summary or transcript is loading. A tiptap editor teardown race ("Cannot read properties of null") is now guarded.
Fixes
- Chat no longer loses earlier messages from the model's memory on long or interrupted conversations. The conversation the model remembered could drift from what the chat showed on screen: a long chat silently dropped its opening turns, a stopped turn left a phantom "Cancelled by user" reply the model learned to imitate, and a turn interrupted by a crash could vanish from the model's view while still showing in the chat. The app now rebuilds the model's memory directly from the same messages the chat displays every time a conversation is resumed, so what you see is always what the model sees. Prompt caching and tool calling are unchanged.
- Foxl Notes no longer crashes to an error screen while an AI summary is streaming in. A timing race during editor teardown could throw "Cannot read properties of null (reading 'cached')" and blank the Notes screen when a summary arrived as the editor was being replaced. The editor now guards against that teardown window.
- Foxl Code task icons now line up vertically when the sidebar is collapsed. In the collapsed icon rail, the Foxl Code task list kept its full-width left-aligned rows, so the task avatars sat off to the left instead of centering in the narrow rail like the nav icons above them. Collapsed rows now show a single centered agent avatar (matching the Chat conversation list), and the expanded list is unchanged.
Fixes
- Long thinking-enabled conversations no longer break with an "Invalid signature in thinking block" error. After the move to structured conversation persistence, resuming a long conversation (especially across an app restart or a model switch) could fail with a Bedrock 400 (
Invalid signature in thinking block). Extended-thinking blocks carry a cryptographic signature tied to the exact moment and model that produced them; replaying those historical blocks verbatim made the provider reject the whole request. Foxl now drops prior turns' thinking blocks when restoring a conversation for the model (the current turn still thinks normally), which is the provider-recommended behavior and verified against real Bedrock. No loss of functionality: reasoning still streams live, it just isn't replayed from history. The relay now applies the same guard on its Bedrock path as defense-in-depth, so older desktop builds and other clients are protected too.
Fixes
- Opus 4.8 now reliably runs tools instead of printing them as text. In longer conversations Opus 4.8 could reply with a literal
[Tool: ...]line instead of actually running the tool. Root cause: prior turns' tool calls were replayed to the model as a TEXT summary glued onto the assistant's message, and 4.8 (the most instruction-following model) copied that shape as its own output. Foxl now persists and restores the full STRUCTURED conversation (real tool-use / tool-result blocks) via the Strands SessionManager, backed by our existing local database, so the model sees past tools as typed actions - not text to imitate. Existing conversations are upgraded automatically the first time they're opened, and conversations now survive an app restart mid-thread. - Type-ahead message queue no longer double-sends or fragments (desktop). When you type follow-up messages while Foxl is working, they queue and splice into the running turn at the next tool boundary (Claude Code style). A dual-path race between the client-side drain and the server-side boundary flush could enqueue the same message twice, or send queued messages one-at-a-time instead of merging them into a single follow-up. Each queued message now has a single owner from the moment you send it, so it is delivered exactly once and always merged with its siblings.
- Same queue hardening on mobile + web (app.foxl.ai, iOS). The relay-mode chat used by the mobile and web apps had the same class of race: a rapid burst of follow-ups could launch concurrent sends (duplicate messages/bubbles), and the queue drain ran its send inside a React state updater, which double-fires under StrictMode. Both now go through one synchronous in-flight latch and drain the whole queue as a single merged turn - delivered exactly once.
- Conversation snapshots no longer duplicate your first message. The structured-persistence upgrade could store the first message of a turn twice (it was written by the app before streaming and appended again by the agent), permanently doubling it in the model's memory of the conversation.
- Memory and skill updates now reach ongoing conversations. Restored conversations were frozen on the system prompt captured when they were first saved, so new memories, skills, and workspace changes never reached them. Snapshots no longer store the system prompt - it is rebuilt fresh every turn.
- Editing or deleting a message now really removes it from the model's context. Edit, regenerate, reset, and single-message delete previously left a stale conversation snapshot behind, so the model could still "remember" turns you had deleted. All four flows now invalidate the snapshot, which is rebuilt from the message history.
- Restored conversations can no longer hit a Bedrock validation error. Snapshots could contain empty text blocks (a side effect of streaming), which Bedrock rejects when the conversation is restored after a restart. They are now filtered out on save and restore.
- Removed a wasteful no-op compaction step. Long conversations triggered a paid "context compaction" model call that operated on a text copy of history the model no longer reads - it reduced nothing while showing a "Context compacted" notice. Context is now bounded by the agent runtime itself (sliding window + automatic overflow recovery).
- Foxl Notes: AI summaries are now sanitized before rendering. A crafted meeting transcript could smuggle active HTML through the AI summary into the editor. Summaries and AI-generated markdown are now sanitized (DOMPurify) with no change to how markdown renders.
- Web/mobile chat storage survives quota pressure. If browser storage filled up (large pasted images/files), conversation saving silently stopped for every conversation. On quota errors Foxl now strips attachment payloads from local history and keeps the newest conversations instead of dropping everything.
Fixes
- Claude Fable 5 works reliably again. Amazon Bedrock flipped which inference profile serves Fable 5: the
global.profile Foxl used since launch now returns server errors, while the standardus.profile (the same kind Opus and Sonnet use) now works from every region. Foxl switched Fable to theus.profile and removed the special us-east-1 routing it needed at launch. Fable now runs through the normal region like every other model, so the intermittent failures are gone. No action needed on your part.
Features
- New model: Claude Sonnet 5. Anthropic's most capable Sonnet - near-Opus 4.8 intelligence at the same Sonnet price ($3/$15 per million tokens), with a 1M-token context window and 128K max output. It is a drop-in successor to Sonnet 4.6: same adaptive thinking, same everything, just smarter. Sonnet 5 is now the default model for chat and the autonomous code orchestrator (Opus 4.8 remains the default for the coding agents and the desktop app), and it is available on the free tier alongside Sonnet 4.6 and Haiku 4.5. Adjustable reasoning effort (low through max) is supported, defaulting to medium - the best speed/quality tradeoff for everyday work. Pick it from the model selector on desktop, app.foxl.ai, Foxl Code, and Foxl Notes.
Features
- Type a follow-up while Foxl is working and it queues, then sends at the next natural break - just like Claude Code. Previously a second message sent mid-response either waited until the whole turn finished or cut the running turn off entirely. Now it is held and spliced in at the next tool-call boundary: the current tool finishes cleanly, your queued message lands as its own turn, and Foxl responds to it - without restarting the work. Queue several and they merge in order into a single follow-up. Press Esc (or Stop) to interrupt immediately instead of waiting. This works on the desktop app and on remote-connected desktops (via the relay tunnel); the cloud-only chat at app.foxl.ai keeps its end-of-turn queue since it has no tool loop to break at. Verified end-to-end against real Bedrock (the queued message is provably consumed only after a completed tool call, never mid-tool) and in a real browser (the queued text renders as its own message and gets its own reply).
Internal
- Upgraded the Strands agent SDK to v1.7.0. The mid-turn queue is built on the SDK's tool-boundary hooks (
AfterToolsEvent/AfterInvocationEvent), using only public APIs - no SDK patching.
Features
- Message action buttons (copy, edit, regenerate) now appear on tap on mobile. On desktop they were already shown on hover. On mobile they were always visible, cluttering every message. Now they start hidden and appear when you tap the message bubble - tap again to hide.
Fixes
- Attaching files with the AWS (Bedrock) provider no longer fails with "The model returned the following errors: ...content.N.type: Field required". Root cause (found via real end-to-end reproduction against Bedrock): with prompt caching on, the agent SDK appends a cache marker to the end of your latest message, and Bedrock rejects that marker when the block right before it is a non-PDF document - which is exactly how a pasted .txt/.csv/.md file lands. (PDFs and images coexist with the cache marker fine.) Foxl now suppresses prompt caching only for a message that carries a non-PDF document attachment, so PDF and image turns keep their cache savings, and the conversation is re-used correctly afterward. Verified end-to-end on the real desktop AWS-provider path (the exact failing request now succeeds, PDFs still cache).
- The relay path is also hardened against malformed attachment blocks. Two related causes were fixed for chats that go through the relay (app.foxl.ai, mobile, relay-routed desktop): (1) some attachments reached the model as a content block missing its
typefield, which the Anthropic API rejects; (2) non-PDF documents (txt, csv, md, json, docx, ...) were sent as native document blocks, but Bedrock's Claude only accepts PDF that way. Non-PDF files are now inlined as text (their contents still reach the model), PDFs stay document blocks, and the relay defensively repairs any malformed block before it reaches Bedrock. Verified end-to-end against real Bedrock. - Attaching two files with the same (or non-Latin) name no longer errors. Bedrock rejects messages with duplicate document names, and the name sanitizer collapsed non-Latin filenames (e.g. Korean) to the same fallback name. Document names are now de-duplicated before sending.
Fixes
- Agent-created terminals now appear in the UI. Terminals opened by the AI agent were invisible in the desktop app: the terminal panel never opened and the tab never appeared. Root cause: the internal WebSocket that listens for terminal spawn/exit events was not sending the connection security token, so the server immediately closed it (code 4003) in production builds. The token is now attached, the connection stays open, and agent-spawned terminals show up and stream live as expected.
- Web/mobile app no longer refreshes when your desktop connects. On app.foxl.ai (and the mobile app) the app starts in offline mode and used to do a full page reload the moment your desktop came online - a jarring refresh that interrupted whatever you were doing. Now the switch to live desktop data is seamless: the chat list and open conversation refresh in place (an in-progress reply is never dropped), and a small "Desktop connected" note appears instead of a reload. Coming back to the app after it was in the background no longer risks a surprise refresh either.
Fixes
- Terminals now sync live everywhere, no matter who opened them. A terminal that appears on your desktop - opened by the AI agent (its terminal tool), a background task, or the desktop UI itself - now shows up and streams its output live on every connected surface: the desktop window AND the web/mobile app (app.foxl.ai). Before, an agent-created terminal could be added to the tab bar without ever becoming the active tab (so it never connected and showed no output), and the phone never learned about a terminal it didn't open itself. Now the desktop forwards every terminal it opens to your connected phone/web, and each surface auto-selects the new terminal so it streams immediately.
Changes
- GLM 5 and Kimi K2.5 are no longer offered on Foxl relay credits. Their quality was below the bar we want for hosted models, so they've been removed from the model picker on app.foxl.ai, mobile, and the web app. They remain available on the desktop app if you connect your own AWS Bedrock account (Settings -> Providers), where they run on your AWS bill rather than Foxl credits and stream natively.
Fixes
- Remote access status is now instant and always accurate. The Mobile/Web page could show your desktop green ("connected") while chat, the channel list, and remote actions all failed with "Desktop is offline" - and even when correct, it could lag up to ~10s behind the real state. Two fixes: (1) the live relay tunnel is now the single source of truth - the desktop's background heartbeat only updates "last seen", while online/offline is owned by the tunnel itself, so the badge can't disagree with whether remote features actually work; (2) the desktop coming online or dropping is now pushed to the web/phone instantly over a live connection instead of being discovered by polling, so the status flips the moment it changes. Even a desktop that dies ungracefully (crash, power loss, network drop with no clean disconnect) is now detected - previously that left the badge stuck green while every request silently timed out; now the connection's missing heartbeats are noticed and the status flips to offline within ~2 minutes (instantly if you try to do something, which fails fast as "offline" instead of hanging). This was also re-built to stay well within free-tier infrastructure limits as usage grows (status rides a near-free idle socket and the heartbeat write is coalesced, instead of every open tab polling on a timer).
- Mobile/Web page: tidied the device controls. The row of unlabeled icon buttons (refresh / check for updates / restart / unpair) is now a single "⋯" menu with clear labels, so it's obvious what each action does. The remote-access on/off switch stays where it was.
- Model selector always reflects what's actually available now. It could keep showing a stale selection cached from a previous session or from when the desktop was connected (e.g. a desktop-only model after the desktop went offline). The list now refreshes the instant the desktop connects or disconnects, and if your selected model is no longer offered it snaps to an available one instead of showing a dead choice.
- Mobile terminal: reverted the iOS keyboard rework that broke typing. The recent change that let you type straight into the terminal on iOS (with a keyboard accessory bar) regressed the experience - tapping the terminal made the keyboard flicker up and down, and Korean/CJK still split into separate jamo. The terminal is back to the previous keyboard behavior on mobile: a dedicated command box that composes Korean correctly, with the quick-key row (Tab/esc/^C/arrows) below it. The terminal chrome redesign and the rotation/resize fixes are kept.
- Stopping a reply now also stops the "Thinking…" shimmer. When you cancelled a response mid-thought, the reasoning header kept its animated "Thinking…" shimmer forever because the cancel path never marked the reasoning block as finished. Cancelling now ends the reasoning block (it switches to "Thought for N seconds") in chat and in the relay/web app, and also tears down a re-attached background stream so it can't restart the animation. (The underlying generation and any in-flight tool were already stopped server-side; this was a display-only leak.)
- Terminal tab status now updates live. A terminal tab's status dot stayed green even after its process had ended - the dot only changed when you deleted the tab by hand, so a shell you exited (typed
exit, the process died, or an agent-spawned command finished) kept looking like it was still running. The dot now turns gray the instant the process exits, in the desktop app and over remote access (Mobile/Web), and dead tabs are cleared when you open a new one. - Terminal: reconnect button + auto-reconnect on resume. Next to the fit-to-width button in the terminal toolbar there's now a reconnect (refresh) button that tears down and re-establishes the connection for the current terminal, re-loading its scrollback - handy when a session looks wedged or shows "[Disconnected]". On top of that, the terminal now reconnects automatically when you bring Foxl back to the foreground: backgrounding the app (especially on mobile) can let the OS drop the connection, and instead of sitting on a dead "[Disconnected]" terminal until you tap reconnect, it re-establishes itself the moment the app resumes (only when the connection was actually lost, so an active session is left untouched).
Changes
- Free credits now refill on your own monthly anniversary. Instead of every free account refilling on the 1st (a date that had nothing to do with when you joined), your 10 monthly credits now top up on your sign-up day each month. If you joined on the 31st, months without a 31st refill you on their last day (Feb 28/29, Apr 30, etc). The Account page shows your exact next refill date (now displayed correctly in all time zones - it previously could read one day early west of UTC). Your welcome credits remain permanent (no expiry) as before.
- Foxl Code: one-tap "Connect GitHub". The connect flow was redesigned to a single button. Instead of choosing between "open GitHub install", typing a GitHub login to adopt, and a dead-end "configure" screen, you now click "Connect GitHub" once, authorize on GitHub, and every organization and account you can access is connected automatically (repos synced, membership granted with your real org role). If your Foxl account had no GitHub identity yet, the authorize step links it. No more guessing which button connects your repos.
Fixes
- Mobile search palette (Cmd/Ctrl+Shift+F) now docks above the keyboard. On phones the global search palette (shared by chat conversations, Notes recordings, and Foxl Code tasks) opened as a full-screen sheet whose results list ran behind the on-screen keyboard - so the list could not be scrolled and the keyboard could not be dismissed without closing the palette. The palette now shrinks to the real visible height when the keyboard is up (the list always keeps a scrollable area above it), and dragging the list retracts the keyboard. Desktop keeps the centered command card.
- Conversation search (Cmd/Ctrl+Shift+F) reads more clearly and focuses the chat you pick. Results now show each conversation's last-activity date instead of a repeated message icon, and stay sorted newest-active first. Opening a result selects it in the left sidebar and scrolls that row into view, even for an older chat that wasn't yet loaded in the list.
- Foxl Code repos list is calmer on mobile. Every repo row used to show a full "Start task" button, repeated down the whole list. On phones each row now shows a single compact chevron; desktop keeps the hover-revealed "Start task" button.
- iOS terminal: Korean (and other IME) input works. Typing Hangul straight into the on-device terminal previously produced broken jamo because the terminal's built-in text field can't commit IME composition inside the iOS web view. The terminal now routes keystrokes through a dedicated invisible composition field and sends only completed characters to the shell - so Korean/Japanese/Chinese type correctly while still feeling like typing directly into the terminal (no separate command box).
- Mobile terminal reflows on rotation. On phones the terminal width was pinned to its initial value, so rotating the device (or showing/hiding the keyboard) left the columns wrong and text clipped. It now recomputes its size on orientation and viewport changes.
- Rate limiting can no longer be burst past under load. The free-tier daily request quota used a check-then-record sequence that two simultaneous requests could both slip through; admission is now a single atomic step, so the quota holds exactly even under concurrent traffic.
- Subscription credit recovery is now once-per-period. A safety net that re-grants a paid subscriber's monthly credits if a renewal webhook is missed could, in some cases, re-grant the full allotment every few minutes once the balance hit zero. Recovery is now locked to exactly one grant per billing period (and correctly ignores already-expired credits), so paid balances refill on the real billing cycle and never double up.
Security
- Foxl Code: stricter input validation on team-member admin actions. The endpoints that remove a member or change their role now validate the target account id format before touching the database - defense-in-depth hardening on the org/team management surface added in v0.3.15.
Security
- Foxl Code: membership is now proven and revocable (SEC-004). Connecting a GitHub organization or account to Foxl Code now requires proving you personally have GitHub access to it (a GitHub authorization step), instead of trusting a typed login. Membership is also continuously re-verified: if you leave a GitHub org, or an admin removes you, your access is revoked and your running tasks for that install are stopped. The Repos page gained a "Who can use this install" panel so you can see exactly which Foxl accounts can act on your repos, with admin controls to change roles or remove members. This closes a cross-tenant access hole and adds the offboarding lifecycle (departures, role changes, team moves).
Fixes
- iOS terminal types directly, like a real terminal. Opening the terminal from the chat bar on iPhone/iPad no longer shows a separate command box below the keyboard. You now tap the terminal and type straight into it, exactly like on desktop, with the quick keys (Tab/Esc/Ctrl-C/arrows) riding on the keyboard itself. This removes the duplicate input surface and makes the on-device terminal feel native.
- Foxl Code: tapping a repo no longer jumps to chat. On mobile, tapping a repo row in the Repos list used to fling you into the new-task composer just from browsing. Repos now only start a task from an explicit "Start task" button (always visible on mobile, hover-revealed on desktop).
- Foxl Code: stop auto-syncing repos on every tab switch. The Repos page used to re-sync (and flash "Synced N repos") every time the tab regained focus. It now only syncs after you actually visit GitHub to change an install, and that implicit sync is silent.
- Foxl Code: disconnect / leave an installation. Each install's access panel now has a gear menu with "Manage on GitHub", "Change repo access", and "Disconnect / leave" - so you can relinquish your own access (and, if you are the last member, fully disconnect the install from Foxl) without uninstalling the GitHub App.
- Foxl Code: back from a runtime returns to Runtimes, not chat. Opening a task from the Runtimes page and pressing back used to drop you on the chat composer. Back now returns to wherever you opened the task from.
- Foxl Code: redesigned repo picker. The repo selector now matches the model picker - a searchable list with a Favorites group on desktop, and a full bottom sheet with tall touch rows + a search box on mobile.
- Account & Notes-settings pages scroll again. Inside Foxl Code (and Foxl Notes) the Account page and the Notes settings page were clipped with no way to scroll to the lower content (usage, billing, delete account). Both now own their vertical scroll inside the shell.
Features
- Terminal redesigned for the new app shell. The terminal panel no longer has its top row clipped when it opens inside the rounded app window, and fullscreen now fills the content area cleanly instead of overlapping the header. The tabs, buttons, and resize handle were restyled to match the rest of the app (rounded controls, consistent hover, smooth motion). On phones the command bar is cleaner and docks right above the keyboard, with bigger, evenly sized quick keys (Tab/Esc/Ctrl-C/arrows) and a proper send button. On iOS those quick keys now ride ON the keyboard itself (a native accessory bar that appears only while the terminal input is focused), so they move perfectly in step with the keyboard like a real terminal app.
- Edit and scroll pasted text in the composer. When you paste a long block of text it collapses into an attachment chip so the input stays tidy. You can now expand that chip to scroll through the whole thing (no more 2,000- character preview cutoff) and edit it inline before sending - trim it, fix a typo, or clear it to drop the attachment. Large pastes stay smooth: only the on-screen portion is rendered, so editing a huge paste no longer lags.
Fixes
- Refreshing during a long reply no longer drops the beginning. When you reloaded the page while the assistant was streaming a long answer, the recovered message could come back missing its opening - only the tail survived. The live recovery buffer is capped to bound memory, and a very long reply overflowed it. Recovery now reads from a separate uncapped record of the in-flight turn, so the whole reply (text, reasoning, and tool calls) comes back intact no matter how long it is. Reconnecting also targets the exact in-flight message, so a follow-up turn can never get glued onto the previous answer.
- Switching chats mid-reply and back now resumes the live stream. If you left a chat while it was still answering, read another conversation, then came back, the reply used to look frozen at wherever it was when you left (it kept generating on the server, but the screen stopped following it until a full reload). Coming back now re-attaches to the live stream the same way a page refresh does, so the answer keeps flowing in. Leaving it running in the background and finishing there still works too - it is marked unread and floats to the top of the list when done.
- Failed scheduled tasks now show as failed. A scheduled task that errored out was still recorded as "completed", which also meant a repeatedly-failing schedule never auto-disabled itself. Failures are now recorded as failed (with the error message), count toward the auto-disable threshold, and the run is still openable as a chat so you can see where it stopped.
Changes
- Personal Brain removed. The For You dashboard's "Today brief" and "Personal Brain" cards and the foxl-brain skill that fed them have been pulled out. For You now shows your upcoming schedules and today's usage. No data you created elsewhere is affected.
Features
- Open any background run as a chat. Scheduled tasks, the Feed Generator heartbeat, and chat subagents now save their full run as a conversation - every step the agent took, the tools it called, and what each tool returned. Click a run in Schedules or Activities (or "Open as chat" on a subagent in Agents) and it opens in the normal chat view with the same tool bubbles a live chat shows. This makes it obvious why a scheduled task did what it did (for example, whether it actually read a skill or where it got blocked), instead of only showing a one-line summary. These run conversations are kept out of your normal chat list and are reached only from those run views, and they are pruned automatically so they never grow without bound.
Fixes
- Deleting a conversation now fully removes it. Foreign-key enforcement is on, so removing a conversation also clears its messages, tool calls, and reasoning instead of leaving orphaned rows behind.
- Subagent and scheduled-run transcripts are faithful and cache-friendly. A forked subagent inherits its parent's conversation so repeated requests reuse the prompt cache (faster, cheaper); the saved transcript now shows only that subagent's own work instead of replaying the parent's. The inherited context is no longer truncated before the model sees it, so the cache is actually reused on longer conversations.
- Mobile: repository names use the full row width. In Foxl Code on a phone, the Repos list no longer clips long owner/name titles behind an invisible button; the name spans the whole row and the row itself taps to start a task.
- Mobile: workspace search fills the screen. The search-all-conversations palette (Cmd+Shift+F, or the header search button on a phone) opens as a full-height sheet on phones instead of a small centered card, so the input is reachable and results use the whole screen. On desktop it stays the centered dialog.
Improvements
- Schedule runs open straight to their transcript. In Schedules, a run with a saved transcript opens as a chat in one click (no expand, no extra button), and a slim "Back to Schedules" replaces the header title while you read it.
- Cleaner run + subagent status. Schedule run rows and the sidebar subagent list now use a single quiet status dot (running, done, failed) instead of competing full-color icons, matching the rest of the app.
- Clearer schedule details. The schedule detail dialog leads with the type, status, and cadence, then lists last run, next run, run count, timeout, and created time as full, untruncated values.
Fixes
- Model responses no longer cut off mid-answer. The relay was parsing Amazon Bedrock's binary streaming format as if it were text, which let a random checksum byte silently swallow the rest of a long response. The stream is now parsed by its real binary framing, so long answers, tool-heavy turns, and code blocks always stream to completion. Verified against a real captured stream that previously dropped 70 of 119 events.
- Mobile chat keeps following new messages. Opening the keyboard or growing the composer no longer turns off auto-scroll, so replies keep scrolling into view without tapping the scroll-to-bottom button. Auto-scroll now only pauses on a deliberate upward scroll and resumes when you return to the bottom or send a new message.
- Foxl Notes mobile home fits the screen. The Record-a-meeting screen no longer overflows sideways: the Start Recording button and recent-note cards stay within the viewport, recent-note previews show clean text instead of raw HTML tags, and the duplicate bottom Start button is gone. Note summaries are now stored as markdown so titles and previews stay readable everywhere.
Improvements
- Settings is scoped to each product. Opening Settings inside Foxl Agent now shows only agent settings; Foxl Notes and Foxl Code each have their own Settings within their product. No more cross-product settings mixed into one list.
Features
- For You dashboard. The Feed page is now "For You": an at-a-glance dashboard with a Today brief, your personal knowledge brain (people, companies, meetings and recent pages), upcoming schedules, and today's usage, with the suggestions inbox below. The sidebar item and all languages follow the new name.
- Personal Brain (beta). Foxl can now maintain a personal knowledge brain as plain markdown in your workspace: people, companies, meetings, concepts, and your own original ideas, cross-linked and consolidated nightly by an opt-in dream cycle. Ask Foxl about anyone you have met and it answers from your own notes, with sources cited and gaps called out. Powered by the new foxl-brain skill; the For You page shows the brain at a glance.
- Account deletion and data export. Delete your account (with full data purge) directly from the Account page, and download your data as JSON or your usage history as CSV at any time.
- Billing lifecycle emails. Welcome email on signup, usage alerts at 80% and 100% of your credits, and a heads-up email if a subscription payment fails, with a banner in the app until it is resolved.
Improvements
- Cold start is much lighter. The main bundle dropped by about 360 kB and the Notes page by about 650 kB (the editor and sub-agent chat view now load on demand), so the app paints noticeably faster on first open.
- Fairer plan changes. Downgrading a subscription now keeps your current credits until the end of the paid period instead of dropping them immediately; refunds claw back only the matching top-up.
- Abuse protection. Per-minute rate limits on the relay, burn-rate monitoring, and welcome credits sized by signup method keep shared capacity safe without affecting normal use.
Fixes
- Sign-in deep links are now CSRF-protected. The desktop and Notes apps reject any sign-in callback they did not initiate, closing a session-fixation vector. Transcription metering is now bound to the recording session server-side.
- Chat streaming cleanup. Fixed a timer leak in the streaming batcher that could fire after leaving the page, and capped the page cache so very long sessions no longer grow memory without bound.
- TypeScript board cleared. Removed seven dead Notes components and fixed all five long-standing type errors; the web app now typechecks clean.
Features
- AI-first Foxl Notes on mobile. The phone experience is redesigned around the AI: a hero Start-recording screen with your recent notes, a full-height live transcript while recording (no more duplicated half-screens), and a new Summary | Transcript | Notes view switcher that lands you on a streaming, read-only AI summary the moment the recording stops. Ask AI is now a labeled button with suggestion chips during the meeting, and the editor becomes an explicit edit mode with a slimmed-down mobile toolbar.
- One motion system across the app. Page and section switches, list items, cards, sidebars, and buttons now animate on a single set of durations and easing curves (the chat language), including press feedback on every button and restored accordion animations. Respects reduced-motion preferences.
Improvements
- Pages load dramatically faster. The statistics dashboard, Workspace, Account, Agents, Settings, Schedules, Activities, Logs, Skills, and Integrations pages now render instantly from cache on revisit and refresh in the background instead of blanking behind spinners. Server-side, the dashboard, workspace tree, workspace search, skills scan, and agent-session queries were rewritten or cached - the worst offenders were full-database aggregations and per-file disk scans running on every poll.
- Chat streaming is smoother. Streaming tokens are batched into far fewer screen updates, completed messages no longer re-render while new ones stream, and auto-scroll is frame-synced.
- Build identity everywhere. Every About/version display now shows the exact build as version + git commit (for example 0.3.10 (dd21869)), and the iOS build number is now the commit count, so TestFlight re-uploads no longer masquerade as a different version.
Fixes
- The 30-day usage chart on the Account page now actually shows 30 days of real data (it silently fell back to 24 hours and dropped every row of the model breakdown).
- Accordion sections animate again (the open/close animation was lost in the Tailwind v4 migration and content has been snapping since).
Fixes
- Opening the sidebar no longer crashes the app. The first v0.3.9 build could blank the whole app (black overlay on mobile, white screen in Foxl Code) the moment the sidebar rendered a non-empty Code task list: the task rows were wired to a sidebar context from the wrong component library, which threw on mount and took the app down with it. The rows now use the shell's own sidebar primitives. A second crash path - an unexpected repo-list response wiping the repo cache and crashing every repo consumer - is also hardened. The over-aggressive document pin (
body { position: fixed }) that fought the sidebar drawer's scroll lock is removed; push-up protection is fully carried by scoped auto-scrolling, the scroll-reset guard, and the native web-view pin. - The mobile app no longer gets pushed up off-screen during recording. On iOS, starting a recording in Foxl Notes (or any streaming/keyboard-heavy screen) could shove the whole app upward so the navigation bar disappeared and never came back. The cause was
scrollIntoViewand focus calls scrolling the entire app shell - and the keyboard plugin snapping the web view to the wrong resting position. Auto-scroll and focus everywhere are now scoped to their own scroll container, a scroll-reset guard snaps any stray document offset back, and the iOS shell pins the web view to its correct resting offset, so the push-up can no longer happen. - The chat usage HUD shows the real prompt size. "Input tokens" now counts the full prompt the model read (uncached input plus cache-read and cache-write tokens) with a "Cached" breakdown, instead of showing only the few uncached tokens - which made a large skill+system prompt read as a nonsensical "2". All numbers come straight from the model's reported usage.
Changes
- Foxl Code Repos is a clean list. The repos page is now a list (with a grid toggle) that leads with the repo name, branch, language, and description, instead of repeating "Connected / Installation #... / tokens are minted per task" and an inline task form on every card. "Start task" routes to the Tasks page; describing a task lives there now.
- One place for the task budget. The per-repo and per-task budget inputs are gone - the default task budget is a single setting in Foxl Code Settings.
- Right-click menus across Foxl Code. Task rows, repo rows, and the task list all have right-click context menus (open, open on GitHub, copy id/branch, resume, cancel, delete, delete all). The always-visible "Delete all" button is gone - deleting is behind right-click now, matching Foxl chat.
Features
- The sidebar conversation list reorders live, with a smooth animation. Sending a message (or a background conversation finishing its reply) now floats that conversation to the top of the sidebar immediately - no reload needed - and rows glide to their new position instead of snapping.
- Foxl Code's model picker now matches the chat one. The composer model picker in Foxl Code (code.foxl.ai and in-app) gains search, sticky provider section headers, right-click favorites pinned to a Favorites group, a bottom sheet on mobile, and live models from the Foxl catalog with Pro-only models badged - the same selector experience as Foxl chat.
- Foxl Notes got a real mobile layout. On a phone, Notes no longer renders the squeezed desktop chrome (header buttons overlapped the tabs, the side transcript dock left a sliver of editor, and picking a note from the sidebar appeared to do nothing). Mobile now has a thumb-reachable bottom action bar (record/stop + timer + pause, AI, summarize, transcript), the transcript opens as a portrait half-split under the editor, the AI assistant opens as a bottom sheet over the editor, and tapping a note closes the sidebar overlay and shows the note.
- The app shell got the Codex-style curve. On desktop the content area now floats as a rounded panel over the sidebar surface - the sidebar blends into the window chrome and curves into the content instead of meeting it at a hard border line. The sidebar drag-resize indicator also stops before the curve instead of running the full window height.
- Dashboard pages share one column width. Usage, Agents, Schedules, Feed, Logs, and Mobile/Web all use the same max width (some were wider or narrower), and the last fully-circular action buttons (Channels/Integrations connect buttons, Feed back button, onboarding close) moved to the standard rounded shape.
- Settings is now a two-pane surface. A left nav with search (flat, borderless style) and consolidated sections - General, Model & provider, Heartbeat, Notifications, Safety, Advanced - plus a Products group that embeds Foxl Notes and Foxl Code settings, so all three products configure from one place.
- The HUD shows per-chat statistics. With a chat open, the title-bar popover shows that conversation's exact input/output/cache tokens and caching-aware cost ("This chat"); otherwise app-wide last-24h totals. Historical $0 costs were backfilled from catalog rates, and the metrics render as label/value rows. All HUD text is localized.
- The app detects a dead local server and restarts it cleanly. If the desktop's agent server stops responding, a quiet overlay offers one Restart button: the app frees the server port (killing any orphaned process tree holding it) and relaunches the server as its own child, identical to a normal boot.
- Refreshing mid-reply no longer loses the streaming answer. The desktop now saves the in-progress reply continuously (not just at tool boundaries), and reconnecting after a refresh resumes from the fuller of the saved draft and the live stream snapshot - including the case where the reply had not started yet (no more gluing new text onto the previous answer).
- "Relay" is now "Mobile/Web" (and "Desktop" when viewed from the web/mobile app). The page header shows a compact status row instead of a giant Online/Offline headline, and the legacy local gateway (self-hosted LAN server) was removed - the Foxl relay is the only remote-access path.
- Settings looks like a modern settings screen. Each section is now one flat panel with hairline-divided rows instead of a stack of floating cards.
- The colorful Integrations and Skills heroes are back. The crossfading per-item gradient backdrops returned behind the sample-prompt cards.
- Buttons and inputs share one shape language. Pill-shaped (fully round) buttons, search fields, selects, and segmented filters across Agents, Schedules, Tools, Skills, Channels, Activities, Logs, Integrations, and the remote-access page now use the standard slightly-rounded shape; status dots stay round. The Agents Run button also dropped its paper-plane icon.
- The status HUD in the title bar is cleaner and stays in sync. Regular UI type in a soft pill instead of terminal-style monospace, and the "Running: tool" / thinking line now resets reliably when a reply finishes (it could previously stick after a reconnect).
- Every page now speaks the chat design language. Skills, Tools, and Channels dropped their centered marketing-style titles for the same left-aligned header Settings and Integrations use; the Skills hero lost its hardcoded gradient (which broke in dark mode) for the flat panel Integrations has; pill-shaped search fields and CTAs became the standard rounded inputs/buttons; and Account, Notification Settings, and Notes Settings now sit in the same centered column as Settings instead of pinned to the left edge.
Fixes
- The agent now splits long shell commands instead of failing silently. The agent's instructions tell it to never inline large content into one exec call (such commands can fail with no output at all) and to build big files in small appended chunks across multiple calls, retrying any silent no-output failure as smaller steps.
- Foxl models and your local AWS provider no longer mix in the model picker. Models served on Foxl credits were labeled by their upstream infrastructure ("AWS"), which split them into duplicate sections and merged them with models from your own local AWS (Bedrock) credentials. All Foxl-served models now group under one "Foxl" section; "AWS" is reserved for the local provider. Within every section models are ordered Claude first, then GPT, then open-weight.
Fixes
- The agent's shell tool no longer silently stalls on long replies. When a model on the Foxl relay produced a long response (for example writing a big file in one command), the relay could drop a piece of the streamed response at a network boundary, so the tool call arrived incomplete and ran nothing with no error. The relay now reassembles split stream frames correctly, so long commands and long answers come through whole.
- The shell tool reports an error instead of hanging on an oversized command. A command longer than the operating system's limit used to fail with no result at all. It now returns a clear error explaining the limit and suggesting writing large content incrementally.
Features
- The model picker groups models by provider with a sticky header. As you scroll the model list, the provider name (AWS, OpenAI, ...) stays pinned at the top so you always know whose model you are looking at. Favorites pin to the top of the list.
- Right-click a model to favorite it. Favorited models pin to a Favorites group at the top of the picker (a star also appears on hover).
- Fable 5 is marked Pro in the picker (like Opus), so it is clear which models the free tier cannot run. The Pro gate is now derived from the relay's tier rules, not hardcoded model names.
- Task Budget moved to Settings. The reasoning toggle and per-model effort stay in the message bar, but the advanced Task Budget control now lives in Settings so it does not clutter the quick picker. Reasoning settings share one source of truth between Settings and the picker, so they can never disagree.
Foxl Code
- "Delete all" is now a visible action at the top of the task list (not only in the right-click menu). Tasks that are still running are kept; only finished tasks are removed.
Design
- Settings and Integrations match the rest of the app. Both pages dropped the centered marketing-style headers, oversized width, and gradient hero in favor of the same flat, left-aligned layout the chat surface uses.
Mobile
- The model picker is a smooth bottom sheet on phones. Instead of a small popup that flipped up from the bottom and crowded the keyboard, tapping the model name now slides up a proper bottom sheet with large tap targets and one-tap effort pills.
Features
- Reasoning controls live in the message bar now. The reasoning toggle, per-model effort levels (Low through Max), and the task budget moved from Settings into the model selector next to the send button, so you can tune reasoning per model right where you pick it. Each model shows exactly the effort levels it supports, picking an effort turns reasoning on, and your choice applies immediately to the next message. The selected effort now shows inline in the bar (for example "Sonnet 4.6 Low") instead of a generic "Thinking" badge, and the menu itself is a tighter, lighter design with the effort levels tucked behind an "Effort" row.
- Search the model list. The model selector now has a search box - type a model name or provider to filter the list instead of scrolling.
Removed
- The "Auto" model option is gone. Auto-routing picked a model by guessing the message's complexity and did not work reliably. You now always know exactly which model answers: pick one and it stays. Anyone previously on Auto is moved to their default model.
Foxl Code
- The send button works as you type on mobile. In Foxl Code on phones (and the iOS app), text you typed was treated as empty until the input lost focus, so the send button stayed disabled and a tap sent nothing. The composer now registers what you type immediately, exactly like Foxl Agent, so send enables the moment there is text and always sends what is on screen.
- No more blank gap under Tasks in the Foxl Code sidebar. On the desktop app, when you had no tasks (or were signed out) the Foxl Code sidebar showed an empty stretch between Tasks and Repos where placeholder rows were reserved. An empty task list now collapses to nothing, matching the Foxl Agent conversation list.
Models
- Fable 5 reasoning is now correct for Foxl Code's autonomous agents too. The Foxl Code orchestrator picked each model's thinking mode by matching on the model name, which could send a new model the wrong shape (Fable 5 only accepts adaptive thinking and rejects the older budgeted shape). It now reads the thinking mode from the same shared model catalog the rest of Foxl uses, so any model added to the catalog gets the right reasoning behavior with no code change.
Security
- Closed two command-injection paths in the local desktop server. The "reveal in Finder/Explorer" and "open folder" actions, and external skill installs, now pass paths and package names as arguments instead of building a shell command string, so a crafted path or skill name can no longer run arbitrary commands.
- Background and channel agents now obey approval gates. Agent runs started by a connected channel (e.g. Telegram), a scheduled task, or the browser extension now go through the same permission deny-rules and approval prompts as the main chat, instead of running tools ungated.
- Sign-in links can no longer be forged on desktop. The desktop only accepts a
foxl://sign-in callback when you actually started a sign-in, so a malicious link cannot silently swap your account. - The local dev server binds to localhost only. In development the API and WebSocket are no longer exposed to your local network by default.
- Closed a WebSocket auth bypass. The browser-extension WebSocket path was matched by a loose substring, so a crafted URL containing "/extension" could skip the connection-token check. The check now matches the exact path.
Fixes
- Windows downloads and auto-update work again. The v0.3.4 release went out with macOS files only, which broke the fixed Windows download links on foxl.ai and docs.foxl.ai and silently stopped auto-update for existing Windows installs. The Windows installer, portable ZIP, and update manifest are back on the latest release, and the release script now carries Windows files forward automatically and refuses to finish if any download link would break.
- Model pricing reconciled. GLM-5 and GPT-4o were priced inconsistently in different places, and Kimi K2.5 had no pricing row on the staging backend (where it failed as "unknown model"). All three are now consistent.
- Sign-in is rate-limited everywhere. The token-refresh endpoint now has the same per-IP rate limit as the other sign-in endpoints.
- foxl.ai shows the current model lineup. The pricing and demo screens named retired models; they now show Claude Fable 5, Opus 4.8, and GPT-5.5.
Foxl Code
- Settings sits at the bottom of the sidebar again. In the Foxl Code workspace, Settings was rendered directly under Runtimes with a large blank gap beneath it. It now pins to the bottom of the sidebar (above your account), matching the Foxl Agent layout, while Tasks / Repos / Files / Runtimes stay grouped at the top. Applies to both the in-app Foxl Code and code.foxl.ai.
Foxl App (mobile)
- Swipe from the left edge to open the sidebar. On the iOS app you can now drag in from the left edge of the screen to slide the sidebar open, the same way native apps do. Tapping outside still closes it. Desktop and web are unchanged.
Fixes
- Reasoning toggle now shows correctly for every foxl.ai model. The model list served by foxl.ai did not carry each model's reasoning capability, so Settings wrongly said "This model does not support adjustable reasoning" for models that do (including Fable 5). The model list now carries the reasoning spec, and the chat path picks the correct reasoning mode from the shared model catalog instead of matching model names, so newly added models work without a code change.
Security
- Google sign-in now requires a verified email. The Google login path did not check Google's email-verified flag before matching an existing account, unlike the GitHub and Apple paths. It now rejects unverified Google emails, closing an account-takeover vector.
- Relay CORS fails closed. When the allowed-origins list was unset the relay reflected any caller's origin together with credentials; it now serves a credential-less public CORS posture in that case (the relay uses header auth, not cookies, so nothing was exploitable, but this is the correct default).
- Desktop no longer exposes the process environment to the web layer. The unused
get-envbridge (which would have carried AWS credentials) was removed from both Foxl Desktop and Foxl Notes, and the standalone Notes window now blocks navigation to external origins. - Foxl Code agents validate repo and branch names before using them in git commands, preventing argument-injection from repository metadata.
Models
- Claude Fable 5 is now available. Anthropic's new state-of-the-art model is selectable across Foxl App, Foxl Code, and Foxl Notes. Fable 5 is built for ambitious, long-running work with advanced vision and is top-tier on most benchmarks. It is a Pro/Ultra model (excluded from the free tier like the Opus tiers). Opus 4.8 remains the default; pick Fable 5 from the model selector when you want the most capable model. Pricing is $10 / $50 per million input / output tokens. Fable 5 is also available through your Claude Pro/Max subscription (Claude Code OAuth) on the desktop.
Foxl App
- Sign in on mobile now finishes cleanly. On iPhone, after signing in through the in-app browser the sign-in sheet stayed open on top of the app showing an "Open Foxl" button that did nothing (the app behind it was already signed in). The sheet now closes by itself the moment sign-in completes, so you land straight in the app. The unhelpful "Copy link" button is gone on mobile.
Foxl Code / Foxl App
- Fixed "Sign in with Apple" failing on the first try. Apple periodically rotates its signing keys; when it did, the next Apple sign-in failed once and only worked on a retry. Sign-in now refreshes Apple's keys and retries automatically, so it works the first time across a key rotation.
Foxl Code
- The task list now sits directly under Tasks in the sidebar, and loads instantly. In the in-app Foxl Code, the left sidebar task list used to drop below the Settings row and take a few seconds to appear (while the right rail was instant). It now renders right under the Tasks nav item, matching code.foxl.ai, and the list starts loading the moment you open Foxl Code (with a brief skeleton instead of a blank gap) rather than waiting for the page to finish loading.
- Editable per-task budget. Settings now exposes the default per-task budget in dollars, so you can set the absolute spend ceiling a single task runs under (previously this was an internal fixed value and only the multiplier was adjustable). The per-task override on the new-task dialog still wins.
- More coding agents. The coding-agent runtime was rebuilt on the AWS AgentCore interactive-shell model, adding Cursor, Hermes, and OpenCode alongside Claude Code, Kiro, and Codex. Each runs in its own isolated microVM and opens a pull request the same way. Cursor brings its own key like Kiro; Hermes and OpenCode run on Bedrock.
Foxl App
- Onboarding logo renders correctly in dark mode. The welcome screen logo was color-inverted in dark mode, which flipped its colors; it now shows the correct mark on any theme.
- Onboarding is fully translated. The "Get the Desktop App" step on the web app was English-only; it now follows your app language in all 10 supported languages.
Foxl Code
- Fixed code.foxl.ai rendering with square corners and wrong spacing. After the Foxl Code product moved into a shared package, the standalone code.foxl.ai stopped generating the styling used only by that product, so the composer, tool rows, and message bar lost their rounded corners and padding. They now render the same rounded, properly spaced UI as the in-app Foxl Code and the desktop app.
- The repo name in the message bar no longer overflows. A long owner/repo name now truncates with an ellipsis inside the composer instead of pushing past the edge, matching the model picker next to it.
- The Files page fills the window. The workspace file browser rendered short with empty white space below it; it now uses the full height in every shell (web, desktop, and in-app).
- Sign-in nudge when you are not signed in. Like Foxl Notes, starting a Foxl Code task now prompts you to sign in (with a one-click sign-in action) instead of the button quietly doing nothing, since Foxl Code runs on your Foxl account and credits.
Site & Docs
- docs.foxl.ai home now lists Foxl Notes. The documentation home page added Foxl Notes alongside Foxl Desktop and Foxl Code, both as a product card and in the browse-by-section grid, so all three products are discoverable from the landing.
- foxl.ai/code refreshed for the in-app launch. The Foxl Code marketing page now reflects that Foxl Code runs inside the Foxl app (web, desktop, and phone) in addition to code.foxl.ai, and a broken docs link was corrected.
Foxl Code
- Foxl Code is now built into the Foxl app. The coding-agent product that runs at code.foxl.ai is now available as a third product inside Foxl, next to Foxl Agent and Foxl Notes. Pick "Foxl Code" from the product switcher to see your tasks, repos, files, and runtimes, open a task to watch its live agent terminal, and start new coding tasks - all without leaving the app, signed in with the same account. The standalone code.foxl.ai keeps working as before.
- A failing screen no longer blanks the whole app. The app now wraps each product view in an error boundary, so if one screen hits an error it shows a recoverable message with a way back to Chat instead of a blank window.
- In-app Foxl Code polish. The Task Plan side panel and chat now read in your app language instead of showing internal text labels; the message bar keeps its send button visible and shortens a long model name instead of pushing the button off-screen; the Task Plan panel gained a close button (with a header toggle to reopen it); and the Runtimes screen no longer scrolls sideways on a phone when it shows a long status message.
Foxl Mobile
- Mobile layout respects the notch and home indicator. On iOS the WebView insets its content for the Dynamic Island / status bar and the home indicator, so headers and the bottom composer clear them. (The earlier attempt to add a CSS-level
viewport-fit=coverinset on top of that was reverted - combining it with the WebView inset desynced render and touch coordinates, so taps landed in the wrong place.) - Foxl Code mobile polish. Agent icons (Claude Code / Kiro / Codex) now load correctly; the content pages (Repos, Runtimes, Settings, Account) get the same page gutter as code.foxl.ai instead of sitting flush against the screen edge; and the Code header is decluttered on phones (the repo picker, which also lives in the composer, is hidden in the header on narrow screens).
- Keyboard-docked message bar on iOS. Tapping the chat box now behaves like the Claude / ChatGPT mobile apps: the keyboard slides up and the message bar rides up to sit directly on top of it, the conversation above shrinks to fit, and the header stays put - no more page scrolling or the input getting hidden behind the keyboard. Applies to both Foxl Agent and Foxl Code chats.
- Foundation for Foxl on iOS and Android. Groundwork for a native mobile app that wraps the Foxl web experience (Agent chat, Notes, and Code) with native sign-in, microphone recording for Notes, and push notifications. Billing on mobile is read-only with an external link to manage your plan on the web. The mobile app is not yet in the App Store or Google Play; this release lays the technical foundation.
Foxl Notes
- Fixed "Start Recording" doing nothing (the actual cause). Recording opens a secure WebSocket to the transcription service, but the production app's content-security policy allowed
https:connections without also allowing secure WebSockets (wss:), so the transcription socket was blocked and recording never started - in production only, while development builds (which have no such policy) worked. The policy now permits the secure WebSocket, so Start Recording connects and transcribes as expected. Production builds also now mirror renderer errors into the app log so issues like this are diagnosable without a debugger. - Fixed the Notes header overlapping on narrow windows. On small or mobile-width windows the Notes/Background tabs and the recording controls could overlap. The recording controls now keep their own space and the tabs shrink, so the header stays readable and tappable at any width.
- Notes now follows your app language, in 10 languages. The Foxl Notes interface used to render in your browser's default language regardless of the language you picked for the app; it now stays in sync with the app's language selector. Added Russian and Arabic translations (Arabic also switches the interface to right-to-left), so Notes is now fully translated in every language the app supports.
Foxl Desktop
- Fixed the sidebar and main view disagreeing after a restart. On a fresh launch the sidebar could show Foxl Notes while the main area still showed the Agent chat (or the reverse). The app now restores both the sidebar and the content to the same product you used last.
Site & Docs
- docs.foxl.ai model and tool pages refreshed. The Models page now lists Kimi K2.5 and shows that GPT-5.5 / GPT-5.4 run on Foxl relay credits (not only via a ChatGPT subscription), and the Agent Tools page tool-profile counts were corrected to match the app.
Foxl Notes
- Fixed "Start Recording" doing nothing (for real this time). The previous release added the microphone permission handling but the production build did not pick up the microphone usage description, so on a signed, hardened build macOS still silently refused mic access and Start Recording did nothing - while development builds worked. The production build now ships the correct microphone usage string, and Foxl asks for microphone access before the first recording (with a clear message pointing you to System Settings if access is blocked). Recording now starts as expected.
Site & Docs
- foxl.ai/note refreshed. Tighter hero copy focused on what Notes does (transcription, AI summaries, action items, any language), and the product mockup now matches the real Foxl Desktop app - the product switcher and recordings live in one shared sidebar, with the recording controls in the app header.
- docs.foxl.ai navigation matches across pages. The documentation pages now use the same top-bar order as the docs home: the section links sit next to the brand, with the search box on the right.
Foxl Desktop
- Fixed GPT-5.5 / GPT-5.4 returning an error on your Foxl account. Picking a GPT-5.x model while signed in with your Foxl account still failed with "Model gpt-5.5 is an OpenAI Responses-API model" in the main chat. The previous release routed most paths correctly, but the primary streaming-chat path still sent GPT-5.x to the Claude-style endpoint, which these models reject. That path now uses the same single routing helper as everywhere else, so GPT-5.5 / GPT-5.4 stream correctly on your Foxl account, in the desktop app and at app.foxl.ai. Claude, GLM, Kimi, and Gemini are unchanged.
Foxl Notes
- Fixed "Start Recording" doing nothing. When Foxl Notes moved into the unified Foxl Desktop app, the microphone permission setup did not come with it, so on a fresh install clicking Start Recording silently failed - the app was never allowed to open the mic. Foxl now requests microphone access correctly (with the proper macOS permission prompt and entitlement), so recording starts and the system asks for mic access the first time, as expected.
Site & Docs
- foxl.ai/note redesigned for the unified app. The Foxl Notes page now reflects that Notes ships inside the Foxl Desktop app - every download points at the single Foxl Desktop installer instead of a separate Notes download, and the standalone-only release notes were removed. One download, one account, one balance of credits.
- Docs top navigation no longer wraps to two lines. Adding the Foxl Notes tab pushed the docs.foxl.ai header wide enough to wrap the section labels; the nav now keeps every label on a single clean row.
Foxl Desktop
- Fixed duplicate messages and empty replies when sending quickly. Firing several messages in a row (or re-sending after a slow turn) could duplicate your message, leave blank assistant bubbles, and bind replies to the wrong message. The send path gated new turns on React state that updates asynchronously, so a rapid burst slipped through and started several streams at once. Turn-start is now serialized with a synchronous per-conversation latch, so a burst becomes one turn plus the rest queued into a single follow-up. The fix is keyed by conversation only - it is model- and provider-agnostic.
- Reasoning controls now adapt to the selected model. The thinking/effort setting was a single fixed list (low/medium/high/max) that did not match what each model actually accepts, and the effort never reached OpenAI-on-Bedrock (GPT-5.5/5.4) at all. Each model now declares its reasoning style and supported effort levels in one place (the model catalog), and both the server request and the Settings UI are driven from it: GPT-5.5/5.4 expose none/low/medium/high/xhigh and now actually send their reasoning effort; Claude Opus exposes low through max (plus task budget); Claude Sonnet exposes low through max; Haiku and GLM/Gemini show a simple on/off; models without reasoning hide the control entirely. Switching models clamps the effort to one the new model supports. GPT-5.x reasoning summaries are not shown on Bedrock yet (a known upstream limitation); the UI says so rather than showing an empty trace, and will surface them automatically once available.
- Fixed GPT-5.5 / GPT-5.4 repeating their answers. Picking a GPT-5.x model (on AWS or through your Foxl account) and asking anything with reasoning on produced an answer that restated itself 3-8 times, growing each time. The cause is upstream: Amazon Bedrock's OpenAI service streams GPT-5.x as several message chunks where each new chunk repeats everything said so far. Foxl now collapses those repeats back into a single clean response - both as it streams and in the saved note - at the point where Foxl talks to Bedrock, so every surface (desktop, app.foxl.ai, Foxl Notes) is fixed at once. GPT-5.4 and Claude were unaffected, and the fix turns itself off automatically once the platform issue is resolved.
- GPT-5.5 / GPT-5.4 now work when routed through your Foxl account. Selecting a GPT-5.x model while signed in to Foxl (rather than using your own AWS keys) failed with "Model gpt-5.5 is an OpenAI Responses-API model." The desktop always sent chat through the Claude-style endpoint, which these models reject. Foxl now detects GPT-5.x and routes it to the correct OpenAI-style endpoint automatically; Claude, GLM, Kimi, and Gemini are unchanged.
Foxl Notes
- Foxl Notes AI summaries support GPT-5.5 / GPT-5.4. Notes shares the same fixed GPT-5.x routing and de-duplication, so picking a GPT-5.x model for a meeting summary produces a clean, single summary instead of a repeated one.
Foxl Desktop
- GPT-5.5 / GPT-5.4 work again when selected directly on AWS. Picking GPT-5.5 or GPT-5.4 with the AWS (Bedrock) provider in the desktop app returned "The provided model identifier is invalid." These models are served only through the native OpenAI Responses API on Bedrock, but the desktop chat path had no branch for the OpenAI-on-Bedrock provider, so it fell through to the Claude Bedrock path and sent the request to the Converse API, which does not recognize the OpenAI model id. The chat model builder now routes the OpenAI-on-Bedrock provider to the Responses endpoint (SigV4, no API key), matching how the relay (app.foxl.ai) already handled it. The relay path was unaffected.
Foxl Code (code.foxl.ai)
- Kiro backend gated off until it has working auth. The Kiro coding-agent backend never had a working headless auth path (the CLI ignores the relay route, has no provisioned key, and its SigV4 mode is unimplemented upstream), so a Kiro task would stall on an interactive browser-login prompt inside the sandbox and end in a confusing failure. Kiro is now disabled at the environment level unless explicitly enabled, the Settings picker greys it out, and starting a Kiro task returns a clear error instead of launching the broken path. Bring-your-own-key scaffolding (paste a Kiro/Q API key in Settings, stored encrypted) is wired in for when the backend is turned on; Kiro runs on your own Kiro/Q account, not Foxl LLM credits.
Foxl Desktop + Foxl Code
- GPT-5.5 / GPT-5.4 streaming fixed on the relay. OpenAI-on-Bedrock models are served through the native Responses API; the relay's streaming proxy could throw an uncaught error and drop the stream when a client disconnected mid-response (the normal case for the Codex CLI and browsers). The passthrough now handles client disconnects cleanly and always meters the tokens it observed, so streaming GPT responses are reliable.
- GPT-on-Bedrock now shows under AWS. In the desktop model picker, GPT-5.5 / GPT-5.4 are badged "AWS" (they run on Amazon Bedrock, like the Claude models) instead of an internal provider name.
- Open-weight gpt-oss models removed. Foxl exposes the frontier GPT-5.5 / GPT-5.4 only; the gpt-oss-20b / gpt-oss-120b entries were dropped from the catalog, pricing, and routing.
Foxl Desktop
- Update "Relaunch" banner now appears on first launch. The "Updated to vX / Relaunch" prompt sometimes only showed up after a manual reload (Cmd-R). The preload cleaned up IPC listeners with
removeAllListeners, so any component unmounting (e.g. leaving the Settings page) silently tore down every other subscriber to the same channel - including the update banner - and the next "update ready" push reached no one. Each subscription now removes only its own handler, and the main process re-emits the ready state on every page load so the banner surfaces deterministically.
Docs (docs.foxl.ai)
- Fixed blank article body and rebuilt the layout. The documentation article column had been rendering blank under the new top section-tabs (a tab bar stretched over the article and painted an opaque panel over the text). Moved docs to a full-width top bar with the section tabs, search, version selector, and a light/dark theme toggle, and put Home / App / Download in a proper page footer that no longer covers the sidebar.
- Foxl Code docs expanded. The Foxl Code section grew from a single page into Overview, How it works, Tasks, Coding agents, GitHub, Budgets and tiers, Autonomous loop, and Resume from review.
- Themed, not just gray. Added Foxl brand blue for links and active navigation, tinted info/warning callouts with their own accent color, and gave code blocks a crisper surface - all correct in both light and dark mode.
Foxl Code (code.foxl.ai)
- Autonomous multi-PR runs. One command now goes all the way: the orchestrator publishes a structured plan (one checkbox = one PR), you confirm ONCE, and from there the autonomous loop fans out a coding agent per checkbox, tracks each PR, retries failures, and marks the plan complete - no babysitting. The confirm button now wakes the loop immediately (it used to dead-end until your next chat turn), a plan carries its own repo + installation so the loop can start it with no live chat, and the heartbeat tick performs the initial fan-out for any confirmed-but-unstarted plan. Autonomy is on by default for every Foxl Code user.
- OpenAI Codex backend (GPT-5.5/5.4 on Amazon Bedrock). A third coding-agent backend alongside Claude Code and Kiro. The orchestrator runs the OpenAI Codex CLI in an isolated AgentCore microVM with all model inference routed Bedrock-direct via the container IAM role (no API key, AWS SDK credential chain). Say "use codex" on a task, or set Codex as your default coding assistant in Settings and pick GPT-5.5 (most capable) or GPT-5.4 (best price-performance). New
forge/agent-codexruntime,agent_backend='codex'end to end. - Per-family coding-assistant settings. Settings now lets you choose a default coding-assistant family (Claude Code / Kiro / Codex) and the model within the Codex family. Tasks with no explicit backend use your default; you can still name a backend per task and the orchestrator honors that. Backends not provisioned in your environment are greyed out.
- Security: webhook task matching hardened. The pull_request webhook now matches a PR back to its task by exact 12-hex-char branch suffix (
substr(id,-12)=?) instead of aLIKEpattern, and rejects non-hex branch names. Closes a path where a crafted branch name on a shared org installation could flip another member's task. - Coding agent always opens its PR on the right branch. The agent re-derives the canonical
foxl/<task>branch at push time instead of trusting the working branch, so a mid-task branch switch never produces a self-referential PR or defeats the task matcher. - Stg now mirrors prod branding. The staging Foxl Code surface shows the real "Foxl Code" name + logo (it was previously white-labelled), so staging is a faithful dress rehearsal.
Foxl Desktop + Foxl Code
- Opus pricing corrected across estimates and docs. Cost estimates, the desktop pricing table, and the docs now all state Opus 4.6/4.7/4.8 at $5/$25 per MTok (cache read $0.50) - matching what the relay meter actually charges. Some estimate/doc surfaces had inherited a stale $15/$75; users were never overcharged (the meter was always correct), but pre-flight task estimates and budget gating were 3x high.
- Adaptive thinking kept on with task budgets (relay path). Enabling a Task Budget on the default relay transport no longer silently drops adaptive thinking; it now layers the budget on top, matching every other transport.
Docs (docs.foxl.ai)
- Claude-style top navigation. The docs site now has section tabs across the top (Get Started, Foxl Desktop, Foxl Code, Reference), each scoping the sidebar to its own tree, plus a refreshed landing page. Old page URLs 301-redirect to their new homes.
Foxl Desktop + Foxl Code
- Claude Opus 4.8 added across the stack. New Anthropic flagship: state-of-the-art coding (SWE-bench Pro 64.3%, SWE-bench Verified 87.6%, Terminal-Bench 2.0 69.4%), longer autonomous runs, lower output variance for enterprise workflows. 4.8 is now the default Opus tier in Foxl Desktop, Foxl Code (orchestrator + coding agents), and the relay model registry. Migration is a model-id swap; same adaptive-thinking shape, same 1M context, same
output_config.effortsemantics. Pricing matches 4.7 ($5/$25 per MTok, cache read $0.50/MTok, cache write $6.25/MTok). - Opus 4.7 stays in the catalog. Users on 4.7 can keep using it via the model picker. The legacy
claude-code/opusshort alias now points at 4.8 (current default Opus tier); the explicitclaude-code/claude-opus-4-7id keeps resolving to 4.7. - Default model bumped.
currentModeldefault in the desktop provider registry is nowclaude-opus-4-8; the model aliassmartandopusresolve to 4.8. - Forge defaults bumped. New users get Opus 4.8 as both
coding_agent_modelanddefault_modelfrom/api/settings. Existing users keep their persisted selection. - D1 migration
0024_opus_4_8_pricing.sql. Seedsmodel_pricingso the relay'smeter.canRequest()resolves 4.8 instead of returning "Unknown model" the way 4.7 did at launch. - Task budgets (beta). Settings page exposes a new
Task Budgetselector (off / 25K / 50K / 100K / 250K / 500K tokens). When enabled with thinking on Opus 4.7+, the desktop attachesoutput_config.task_budget = {type:'tokens', total:N}and thetask-budgets-2026-03-13beta header to the AnthropicModel-backed transports (Anthropic API direct + foxl relay). Bedrock Converse silently dropsoutput_configtoday, so the budget is a no-op on the direct-Bedrock path; we will switch to Bedrock Mantle Messages API later to fix that. - Sampling-param hardening for 4.7+. Anthropic provider's
complete()andstream()no longer sendtemperature: 0.7when the model id matches Opus/Sonnet/Haiku 4.7+. Those models reject non-defaulttemperature/top_p/top_kwith HTTP 400 per the field guide; Foxl now lets the API use its calibrated default. Older Claude/non-Claude paths are unchanged. resolveEffectiveContextWindowrecognizes 4.8. Like 4.7, Opus 4.8 reports the catalog's full 1M context on the Claude Code OAuth transport. Sonnet 4.6 and Haiku 4.5 are still capped at 200K on the subscription pool.- Account / Usage charts add 4.8. App.foxl.ai AccountPage and code.foxl.ai UsagePage label and color the new tier so the per-model bar charts don't fall back to "claude-opus-4-8" raw text.
Foxl Code (code.foxl.ai)
- Goal-first task rows. Both the left Tasks sidebar and the right Task plan rail now lead with the goal (title or prompt preview) instead of the repo name. The agent's avatar carries its lifecycle status as a dot, repo + timestamp drop to a muted second line. Three tasks in the same repo no longer read like the same row repeated.
- Account menu in the topbar. Sign-in / Sign-out, credits balance, Service status, and account links live in a dropdown under your avatar. The Orchestrator-degraded indicator surfaces here when
/healthreturns non-OK so you know new tasks may queue. - Right rail polish. "Open in full page", "Back to tasks", and a "Prompt" header on the task plan rail. Search bar inside the rail. Accessible Open/Close labels for screen readers.
- Edit your display name. Account page now lets you set the name shown across Foxl. Saves through the relay's new
PATCH /auth/meendpoint, so the name follows you to Foxl Desktop too. - Quieter Runtimes page. Removed the bottom "Recent activity" event log; the active-runtime grid stays. Each runtime card still shows its own mini-terminal preview of the last few events.
Foxl Desktop (app.foxl.ai)
- Display-name editing in Relay-Only mode. Profile edit now persists to the Foxl account on relay (not just local state). The new name carries across browsers, the Foxl Code SPA, and Foxl Desktop on macOS / Windows.
Relay
PATCH /auth/me. New authenticated endpoint to updatedisplayName(max 100 chars; passnullor empty to clear). CORS allow-list now includesPATCH.
Foxl Code (code.foxl.ai) - operational
- Default per-task budget floor raised to $500. Previously a 30-min Opus task auto-estimated at ~$1.30 and got killed mid-flight with
forge task budget exceededeven when the user had thousands of credits. The auto budget now floors to $500 (override + multiplier paths still respected); daily-cap default lifted from $0.50 to $500 with a $1000 ceiling.
Foxl Desktop
- Streaming-text shimmer restored to v0.2.30 timing. The Tailwind v3 -> v4 migration shipped in v0.2.41 lost the original keyframes; v0.2.42 wrote them back at 1/4 of the original travel range, which made the shimmer "tick" visibly at the seam every half-second. Keyframes now match the v0.2.30 config (200% -> -200%) - smooth, uni-directional sweep, no visible loop. Shared
@foxl/uiShimmer also now sets the duration on every variable name in use across the workspaces.
Foxl Code (code.foxl.ai)
- Cmd+Shift+F global task search. New CommandPalette +
GET /api/search/tasksendpoint. Match across title, repo full name, prompt body, and id; results scoped to the user via the relay JWT. Hard-capped at 50 results, 150 ms debounce. Press Enter (or click) to navigate to the matched task. - Cmd+F in-chat search. New ChatSearchBar mounted on TasksPage and TaskDetailPage. Walks text nodes inside the chat container, highlights matches via the CSS Custom Highlight API, and steps prev/next with Enter / Shift+Enter. Falls back gracefully on browsers without the API.
- Auto-refresh sidebars on task mutations. New
TASKS_CHANGED_EVENTwindow event fires after every cancel / set-status / retry / resume / delete / delete-all. AppSidebar inline list and TaskDocSidebar In-progress/Recent sections listen for it and refetch immediately, so right-rail and left-sidebar both stay in sync without waiting for the 5 s poll.
Foxl Code (code.foxl.ai)
- Kiro CLI as a peer coding-agent backend (INC-017). New
forge/agent-kiroAgentCore Runtime container ships the AWS-distributed Kiro CLI (kiro-cli + kiro-cli-chat + kiro-cli-term). The orchestrator picks per task via the newagent_backendzod arg onspawn_agent, defaulting to Claude Code unless the user explicitly says "use kiro". Settings shows backend availability read-only; the orchestrator owns the per-task choice. Kiro auth (KIRO_API_KEY / SIGV4) is still pending upstream so Kiro tasks land at the login prompt today. - Per-task agent backend persisted on tasks rows. New
tasks.agent_backendD1 column captures which CLI ran each task. RuntimeCard + TaskDetailPage header render the backend icon (Claude Code orange mascot vs Kiro logo) so post-mortem context survives even if defaults change. - Bulk Delete-all from right-click context menu. New
DELETE /api/tasksendpoint wipes every terminal-state task (queued/running rows skipped, returned in the response). Right-click any task row in the AppSidebar inline list, or anywhere in the right-rail TaskDocSidebar list, to surface "Delete all". - Repos page connection flow. "Add another install" is now a dropdown with two paths: open-on-GitHub (the standard flow) and Connect-by-login (adopt an install Foxl never picked up). Each install card gets an "Add repo" button that jumps to GitHub's per-install Configure page; the Repos tab auto-syncs on focus when you come back. Manual-add escape hatch reachable even when installs already exist.
- One-click GitHub repo jump. The topbar repo picker now has an external-link button next to the trigger that opens the selected repo on github.com. Each row inside the dropdown also gets a hover-revealed open-on-GitHub action.
- Orchestrator session bumped v6 -> v8. Forces AgentCore to spin up fresh containers so the new
spawn_agentschema + Kiro-aware system prompt take effect immediately. - Relay proxy: strip Bedrock-incompatible beta fields. Claude Code 2.1.x sends
context_managementandoutput_configin every request body. Bedrock InvokeModel rejects them with 400 "Extra inputs are not permitted", which the SDK retried until the agent gave up. The relay now strips these fields before forwarding to Bedrock. - Agent API key prompt eliminated. Pre-seed the per-task JWT into Claude Code's
customApiKeyResponses.approvedso the "Detected a custom API key" interactive prompt never fires. Previously the agent stalled on this prompt every invocation. - Resume without alert. The "Resume" button no longer pops a text-input dialog. It flips the task back to running immediately and the user types directly in the live terminal.
- Resize-jiggle on mount and resume. The terminal sends a shrink-then-restore resize pair (two SIGWINCHes) on every page load and on resume, forcing Claude Code to do a full repaint. Fixes partial rendering on fresh loads.
- Deduplicated task events. Removed SSE backlog replay from the Durable Object subscriber. Events are now delivered once via the REST backlog, then live-only via SSE. Timestamps no longer mismatch.
- "live" badge restricted to running tasks. The sidebar badge now only shows on running/queued tasks, not review (where the terminal is idle).
- Compute usage metering. The agent container now emits
wall_clock_secondsandio_wait_pctin the finished event. The relay uses this to calculate and deduct AgentCore vCPU + memory credits alongside LLM token credits. - Expanded agent container toolbox. Added bash, vim, nano, jq, ripgrep, htop, tree, ping, dig, nc, gpg, ssh, rsync, and more to the agent Docker image.
- Telemetry isolation. Agent containers set
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1so per-task JWTs are never sent to api.anthropic.com.
Foxl Desktop (app.foxl.ai)
- Shimmer effect restored. The streaming text shimmer animation (
animate-shimmer) was lost during the Tailwind v3-to-v4 migration. Keyframe and utility definition re-added to index.css.
Foxl Code (code.foxl.ai)
- Autonomous loop. Your coding agents now check back on running tasks automatically. Enable it in Settings - the orchestrator wakes up on a configurable interval (default 5 min), checks if anything has progressed since its last look, and follows up if needed. Quiet hours and a daily spend cap keep it from running up costs overnight.
- Per-task spend metering. Every LLM call a coding agent makes is now tracked in real time against the task's budget. You can see cumulative cost on the task detail page. When a task hits its budget ceiling the agent stops gracefully - no surprise charges.
- Custom budgets. Set a spending limit before launching a task. The composer now has a "Budget (USD)" field. Values below the auto-estimate are rejected; the system caps runaway spend at whatever you set.
- Resume from review. When a coding agent pauses for your review (e.g. waiting for approval on a risky change), you can now hit "Resume" to continue exactly where it left off - same container, same git state. If the container was reaped, the UI falls back to a retry automatically.
- Agent screen and input. The orchestrator can now read a running agent's terminal screen and send input to it. This is the foundation for multi-agent coordination - one agent can check on another and unblock it.
- Dual storage architecture. Two orchestrator runtimes: Session Storage (default, per-session physically isolated) and S3 Files (shared mount with prefix-based isolation). Pick in Settings.
- Stop button. Cancel streaming responses mid-flight via the composer's stop button.
- Thinking block interleaving. Multiple thinking blocks render independently with separate collapsible sections.
- Tool call shimmer fix. Tool result events now correctly stop the running animation.
- Workspace page file browser. Browse your agent's working directory - session storage or S3 Files mode detected from settings.
- Mobile: compact New Task button. The topbar "New Task" button collapses to a "+" icon on small screens so it no longer overflows.
Foxl Desktop
- Monorepo workspace fix. Independent deploy units (docs, site, agent) no longer block the shared workspace install. Speeds up
npm installand removes the linux-only platform error on macOS dev machines.
Infrastructure
- Per-task JWT replay window. Relay now checks the task's D1 status before honoring a per-task token. If the task is already in a terminal state (merged/failed/cancelled), the token is rejected even if it hasn't expired - prevents spend after completion.
- Heartbeat cron (1-min global trigger). A single Cloudflare cron fires every minute, prefilters eligible users from D1, and fan-outs to per-user HeartbeatDOs. The DO handles interval gating, quiet hours, daily cap, and event-sequence dedup so idle users cost nothing.
- Budget multiplier and daily cap columns. D1 migrations 0021-0022 add per-user budget_multiplier, heartbeat_interval_seconds, daily_cap_usd, and quiet-hours fields to forge_user_settings.
- Session storage orchestrator runtime. New AgentCore runtime with sessionStorage at /mnt/workspace (14-day idle expiry, per-session isolated).
- Terraform apply clean in stg workspace. IAM policy updated for new AP ARN.
Cross-product
- Foxl Desktop and Foxl Code now share one version line. Both products ship under the same v0.2.40 tag. Every workspace
package.jsoncarries the same"version". Per-product version drift removed. - Stg environment for every customer-facing surface.
stg.foxl.ai,stg.app.foxl.ai,stg.code.foxl.ai,stg.relay.foxl.ainow run the same source as prod with separated D1 (foxl-relay-stg), JWT secrets, GitHub App (foxl-bot-stg, App ID 3747233), AgentCore Runtime (foxl_forge_shared_stg-CQYYNv28k2), and S3 bucket (foxl-forge-repos-stg). Stg cannot reach prod and vice versa. Eachwrangler.tomlexposes an[env.stg]block;--env stgflips the deploy. forge/web carries a yellowSTAGINGbanner only when built withVITE_FOXL_ENV=stg(dead-code-eliminated for prod). Marketing/docs has no stg. - Docs versioning starts at v0.2.39.
docs/scripts/snapshot-version.mjssnapshotscontent/docs/tocontent/docs/v<version>/before edits land for the next release. Old versions stay reachable atdocs.foxl.ai/v<version>/....
Security
- GitHub login account-takeover bypass closed. The relay's GitHub OAuth callback used to match the foxl user by email, so anyone who controlled a GitHub mailbox could log in as a foxl account that was created via magic link or another provider. Login now matches by GitHub's immutable numeric
id(stored in the newusers.github_user_idcolumn, migration 0017) and refuses to auto-link to a pre-existing email-only account. Affected accounts are asked to sign in with their original method first and connect GitHub from settings. - Cross-tenant install ownership transfer closed.
INSERT INTO github_installations ... ON CONFLICT(installation_id) DO UPDATEno longer touchesuser_id, and/api/github/setupnow refuses to write when the install is already owned by another foxl user (audit-logged ascross_tenant_denied). Combined with the existing/api/installations/adoptownership check this closes the "Foxl user A reclaims Foxl user B's install" path. FORGE_WEB_URLenv wired through OAuth callback. Three hardcodedhttps://code.foxl.ai/tasksredirects inrelay/src/auth.tsnow consultc.env.FORGE_WEB_URLfirst (default unchanged for prod). Stg setsFORGE_WEB_URL=https://stg.code.foxl.ai. Closes the leak where stg-originated GitHub OAuth would land tokens on prod code.foxl.ai.- Repo selection mode reclassified. Forcing foxl-bot installs to
repository_selection: "selected"was on the launch-block list; that conflicts with the multi-tenant SaaS model where each user installs on their own account/org and picks the mode at install time. Tenancy is enforced at thegithub_installations.user_idlayer (single owner per install, ON CONFLICT does not change owner, audit log on every mint). A user who picksallonly enables Foxl Code on their own repos under their own install. Removed from the launch checklist;forge/SECURITY.mdandforge/TODO.mdupdated.
Foxl Code (code.foxl.ai)
- Mobile layout pass on every page.
AppShellcontent padding drops frompx-6 py-8topx-4 py-6on phones (matches app.foxl.ai), so the 375px viewport stops cropping cards and headers. Page-level fixes: TaskDetailPageaction bar (Pull request / Issue / Cancel buttons) now wraps to a second row instead of overflowing on phones; the header collapses to a stacked layout, the meta + budget cards switch to a 2-up grid above the terminal on tablets and 1-up under the terminal on phones, and the terminal's fixed68vhshortens to60vhso soft keyboards don't push the composer offscreen.ReposPagerepo grid drops to one column on phones (was breaking atmd:only); the Sync + Connect buttons wrap when the heading is wide.RuntimesPageactivity rows wrap the repo name under the kind label on small screens; horizontal padding tightens.SettingsPagecard padding goesp-4 sm:p-6; the Save / Sign-out row wraps so neither button gets clipped.UsagePagestat cards switch to a 3-up grid atsm:(waslg:-only), and ledger rows wrap when the timestamp wouldn't fit.OnboardingPageouter padding drops topx-4 py-10and the heading scalestext-2xl sm:text-3xl.
Build
- Foxl Desktop is no longer hoisted into the same
node_modulesas Foxl Code.foxl/apps/webandfoxl/apps/electronwere removed from the rootworkspaceslist; they install their ownnode_modulesand pin Tailwind v3 again. Foxl Code stays on Tailwind v4 in the root workspaces tree. Stops the cross-product break where forge bumping Tailwind silently dragged the desktop CSS pipeline along. legacy-peer-deps=true+NODE_ENV=productionno longer silently skips devDependencies. A stale shellNODE_ENV=productionwas makingnpm installresolveomit=dev, which leftvite,@vitejs/plugin-react,@tailwindcss/vite,esbuild, and friends out ofnode_moduleseven though they were inpackage-lock.json. Builds (npx vite build,node bundle-server.mjs) then crashed withERR_MODULE_NOT_FOUND. The integrity issue is now resolved againstnode_modules/.- Removed the duplicate
site/landingentry in the rootpackage.jsonworkspaces list. npm tolerated it but it pollutednpm lsand slowed install resolution. scripts/release-all.shdocs deploy passes--project-name foxl-docsso wrangler 4.92+ does not fail with "Must specify a project name."
Fixes
- app.foxl.ai white screen fixed. A vite chunking quirk was bundling two copies of React into the relay-only build; the second copy hit the
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADEsymbol mismatch on boot and rendered nothing. The build now dedupes React across the workspace symlink and lands all ofreact,react-dom, andreact/jsx-runtimein a singlevendor-reactchunk. Verified live: zeroCLIENT_INTERNALSreferences invendor-radix/vendor-ui/indexchunks. - Sidebar borders no longer render black, electron no longer starts in dark mode.
index.csswas using@import '@foxl/ui/styles.css'after@font-faceand@tailwinddirectives, which PostCSS silently drops. Every--background/--border/--sidebar-*HSL var was undefined; tailwind'sborder-borderresolved tohsl()(invalid) and the browser fell back to black. Tokens are inlined intoindex.cssagain so the rule order does not matter.@foxl/ui/styles.cssstays the canonical source for forge/web.
Cross-product
- Foxl Code (code.foxl.ai) shipped as a separate product on the same monorepo. Foxl Desktop and Foxl Code share
relay.foxl.aifor auth and the credit pool but version independently. SeeVERSIONING.mdat the monorepo root for the full strategy. Foxl Code release tags carry theforge-vprefix so the two products do not interfere. packages/uinow exposes shared layout primitives (AppShell,NavSidebar,TopBar,Breadcrumbs) consumed by Foxl Desktop and Foxl Code so the chrome stays in sync between the two products.- React bumped to 19.
foxl/apps/webandpackages/uinow agree onreact ^19.0.0andreact-dom ^19.0.0; recharts bumped to^3.8.1to keep peer-dep alignment. No user-visible UI change beyond the rendering fixes above.
Foxl Code (code.foxl.ai)
/tasksrebuilt around a bottom-pinned composer. Active tasks (queued/running/review) and recent five closed tasks render above the composer on the empty-state view, with an 8s background poll so status changes show up without a manual refresh. Pressing Enter now surfaces a clear inline error if no repo is connected or no paste is typed - it used to swallow the keystroke silently. Submit + topbar GitHub buttons flattened (no shadow / border)./repospopulates after install. The orchestrator gained a state-bound install URL handler, a manualPOST /api/installations/syncendpoint, anadopt-by-owner endpoint for the legacy install path, and webhook handlers forinstallation+installation_repositoriesso future installs sync without manual action. The Repos page auto-syncs once when installations exist with no repos and exposes a "Sync repos" button. Empty-install state shows both "Sync repos" and "Configure on GitHub".- Workspace page added at
/workspace. Per-user view of the shared S3 Files mount (/mnt/repos) - finder-style lazy tree, resizable sidebar, search across loaded files, context menu (Copy path), text / binary preview with a 1 MiB inline cap. Shape ported from Foxl Desktop's WorkspacePage, data layer swapped for the orchestrator's new/api/files(S3 ListObjectsV2 + GetObject via aws4fetch). Read-only - the AgentCore container owns writes. Tenant isolation is the bucket prefixusers/{user_id}/{org}/{repo}/...enforced server-side. - Terraform shared S3 Files mount enabled.
forge/infra/terraformnow provisions the VPC, NAT gateways, S3 bucket + S3 Files file system, mount targets per AZ, IAM service role, and the access point. AgentCore endpoint flipped from PUBLIC to VPC mode so the shared cache is reachable. Updated CLAUDE.md to reflect that Pro/Ultra share one mount with prefix-based tenancy (was previously documented as Enterprise-only).
Gateway "Enable remote access" actually works now
- Click was a no-op when no access token existed. The
tunnel-toggleIPC handler returned{ok: true}immediately and only kicked offconnectTunnel(at, rt, ...)ifatwas truthy. If the user wasn't signed in,atwas empty, the function silently bailed, and the button looked frozen forever. - Handler now
awaits the localStorage read, returns{ok: false, reason: 'no-access-token'}when the token is missing, and the renderer surfaces a "Sign in first (Account page)" message inline beneath the button. - For other failures the handler returns
{ok: false, reason: '<exception text>'}instead of pretending success. - Renderer polls
tunnelStatus()for up to 15 seconds after a successful start, refreshesfetchAll()once the WebSocket attaches, and shows a clear timeout message if it never does. - Loading state on the button - spinning Power icon + localized "Connecting…" copy + disabled while awaiting, so a click no longer looks like a no-op.
Gateway page i18n
- Gateway "No desktop" copy now resolves in every locale. v0.2.37 added new English strings (
relay_no_desktop_desc_v2,relay_turn_on_desktop,relay_desktop_not_connected,relay_desktop_not_connected_desc,relay_enable_remote_access) but didn't add them to the other 9 locale files - users on Korean / Japanese / Chinese / etc. saw raw key names. v0.2.38 ships translations for all 10 locales (en, ko, ja, zh, es, fr, de, pt, ru, ar) plus the new connecting/error strings.
Sidebar tooltips
- Collapsed sidebar (Cmd+B) now shows the conversation title as a dark tooltip on hover. Previously the title only rendered when expanded; with the sidebar collapsed to icons there was no way to read which conversation was which. Tooltip mirrors the Cmd+B nav-item style.
- Top-level nav items and the settings group also surface their label as a tooltip when collapsed - Chat, Workspace, Usage, Settings, Account. Same dark popover styling.
- Truncated rows show a hover tooltip with the full text in both the sidebar conversation list and the Workspace file/folder tree. The tooltip only opens when the inner span actually overflows (
scrollWidth > clientWidth); short titles don't trigger it. 700ms delay so it's not noisy.
Workspace polish
- Editor header reads
MEMORY.mdinstead ofWORKSPACE / MEMORY.md. The redundantWORKSPACE /crumb prefix was removed - the sidebar already says WORKSPACE, the breadcrumb just doubled the label. - File and folder rows restored to v0.2.15-era density (
text-sm/py-1.5). The earlier compaction totext-[13px]made the tree feel cramped against the rest of the app.
app.foxl.ai conversation pagination fix
- Sidebar conversation list now pages past the first 50. On app.foxl.ai (relay-only mode with desktop tunnel online) the IntersectionObserver attached to the conv scroll container saw
convScrollRef.current === nulleven after the list had rendered, so the "load more" sentinel never fired. Switched to a callback-ref + state pattern so the effect re-runs the moment the scroll element actually mounts, plus replaced the IntersectionObserver with a plainscrollevent listener for reliability inside the nested hidden-scrollbar container.
Gateway page "No desktop" actions
- Web (app.foxl.ai): replaced the lone Download button with two CTAs - "Turn on desktop" (deep-links to
foxl://opento launch the installed app) and "Download for macOS" (fallback if the app isn't installed yet). - Desktop: when looking at /gateway from inside the installed app, the page no longer suggests downloading the desktop app (you're already in it). Instead it surfaces an "Enable remote access" button that flips the tunnel toggle, with copy explaining what relay connection gets you.
max_tokens overhaul (Claude Code parity)
- Every Claude path now requests the model's full output capacity. Previously the desktop chat sent
max_tokens: 4096whenever thinking was off, the agent layer hard-coded8192for every provider, and the relay-only web app capped output at64000regardless of model. Long generations were silently truncated. Now Opus 4.7 / 4.6 ask for 128k, Sonnet 4.6 / Haiku 4.5 ask for 64k, matching what the Claude Code CLI sends. - Agent layer reads from the catalog instead of a hard-coded constant.
strands-agent.tsnow resolvesentry.maxOutputper model for Bedrock, Anthropic, OpenAI, OpenAI-OAuth, Claude Code OAuth, and the foxl relay path. - Claude Code OAuth catalog corrected. The 32k metadata was misleading - real requests already went out at the catalog max. Updated to 128k (Opus) / 64k (Sonnet) so
/contextpercentages and autocompaction triggers reflect actual behavior.
Relay billing fixes
- Opus 4.7 unblocked on the relay. D1
model_pricingwas missing a row forclaude-opus-4-7, someter.canRequest()rejected every request with "Unknown model" - even though the registry listed it. Migration0011_opus_4_7_pricing.sqladds the row. - Opus pricing corrected to Anthropic's published rates. Catalog and pricing previously inherited Opus 4.1's $15/$75 per MTok. Both Opus 4.7 and Opus 4.6 are $5/$25 per MTok. Updated
model-catalog.tsand the local D1 row.
Sidebar UX fixes
- Cmd+N (and the File → New Chat menu item) always opens a fresh chat. Previously, pressing Cmd+N from a non-chat page resurrected the previous conversation because
navigate('chat')re-pushed/chat/<oldId>from a stale closure value, and the popstate handler reloaded that conversation. - Clicking a different conversation actually opens that conversation. Selecting B from the sidebar after viewing A used to re-load A - same closure-staleness bug routed
/chat/<A>instead of/chat/<B>. Now the click goes throughnavigate('chat', { conversationId: id }), which uses the explicit clicked id. - A → B switching no longer flashes empty state. Conversation messages stay on screen during the fetch; the new conversation's messages swap in once they arrive. No suggestions/greeting flicker between selections.
- Conversations are no longer highlighted while you're on Workspace, Usage, or other non-chat pages. Sidebar conversation rows now check
activeNav === 'chat'before applying the active style. Clicking the top-level Chat nav from those pages opens a new chat instead of resurrecting the previously-selected one. - Infinite-scroll loader no longer spins forever when conversation pagination fails. On app.foxl.ai (relay-only mode) the desktop conversation list endpoint isn't reachable; the failed fetch used to leave the sentinel mounted, retriggering the IntersectionObserver in a loop. Now any non-OK response or thrown error sets
hasMoreConversations: falseand the spinner goes away.
foxl.ai migrated to Next.js SSG for SEO
- Google can now index foxl.ai. The landing page was a pure CSR React SPA - crawlers saw empty HTML. Migrated to Next.js 15 App Router with static export. Every page is pre-rendered as full HTML with proper meta tags, canonical URLs, and Open Graph data.
- JSON-LD structured data for sitelinks. Organization, WebSite (with SearchAction), SoftwareApplication, and SiteNavigationElement schema in root layout. Signals Google to show the site hierarchy (Blog, Pricing, Security, Changelog, Docs) as sitelinks beneath the main search result.
- sitemap.xml with 22 URLs. All static pages + 17 blog posts. robots.txt allows AI crawlers (GPTBot, ClaudeBot, PerplexityBot) and blocks training-only crawlers.
- worker.js simplified. Removed the entire SPA routing layer (HTMLRewriter meta injection). Next.js handles per-page metadata natively. Worker now only handles security headers, llms.txt proxy, and old .html redirects.
- Blog preserved exactly. Same thumbnail grid, pagination, category filters, search, and individual post layout as before.
Landing page redesign
- Pricing moved to dedicated /pricing page with detailed credit breakdown, BYOK explanation, local model info, and FAQ.
- Security section redesigned. Clean 3-column architecture diagram with vertical mobile connectors, prose-style feature descriptions replacing card grid.
- "What people ask" replaced with "What other agents can't do." 6 quote-heavy cards condensed to 3 focused differentiators: mobile coding, 24/7 background agent, multi-provider single interface.
- FAQ trimmed from 11 to 5. Ordered by user priority (features, competitors, pricing, privacy, mobile access). First item open by default. Links to docs/Discord for more.
Agents page bug fixes
- Running agents no longer flash as "completed" the moment the detail view opens. AgentChatView receives
initialStatusfrom the Agents page (source of truth) so it starts with the correct status the user already saw, eliminating the race where in-memory state clears before the detail view fetch resolves. - Assistant responses stop rendering twice.
broadcast()was double-recording every agent event to DB (once inside broadcast, once in the runAgent callback). Removed the redundant call. Also,runAgent()no longer emits intermediatemessageevents for no-tool turns - onlycompletecarries the final response. - New agents started from the Agents page now show their progress events. Session ID minted up-front so WS broadcasts and DB rows share one ID.
Relay, Schedules, Feed, Activities, Logs redesigned
- Unified hero-stat pattern across five pages. Each page now opens with a single headline answer ("12 active schedules", "Live: streaming to 2 clients", "9 unread items", "247 events", "1,248 lines streamed") plus three supporting stat cells — the same rhythm as Agents and Usage. Replaced the nested Card-in-Card chrome with flat sections separated by dividers.
- Relay (Gateway) rebuilt. Flat device topology with a single connected-desktop row, inline quick-actions (update / restart / unpair), web and mobile clients as tree branches off the bottom edge, and audit history in a collapsible scroll panel below. Tunnel "Remote access" is now a pill toggle next to Refresh instead of a standalone card. Legacy local gateway drops into one collapsible at the bottom for advanced users. Cleaned up dead code from the deleted pairing-code flow.
- Schedules stopped pretending to be a spreadsheet. Dense table became a divided list sorted enable-state-first, then by upcoming-run proximity. Each row leads with a status dot, the human-readable cron (raw expression lives in the tooltip), and prefers "next in 18m" over stale "last ran 3h ago". 7-day success rate and average runtime are now prominent hero stats. Recent Runs uses pill inputs and chip filters, grouped by day bucket. Heartbeat rows are labeled system-managed and can't be deleted.
- Feed reads like an inbox. Segmented pill toggle (Inbox / Archive) with per-view counts. Each item is a single clickable row — click archives, click again unarchives. Urgent dot goes amber, unread dot stays foreground, archived items fade. Hero leads with unread count and amber urgent subtext when applicable.
- Activities chip filter. Time range matches Agents' pill style; type filter is a horizontal chip bar derived from actual event frequencies so users can focus on tool_start vs thinking vs error without a dropdown. Outer Card wrapper removed so the list can breathe.
- Logs terminal has a frame that doesn't fight it. Hero shows stream state (live vs paused+buffered count) plus error/warning/info totals. Level filter is a segmented pill; source filter is a chip row behind the Filter icon. Console is one bordered rounded-xl panel instead of a Card wrapping another Card.
- UX ⇄ API alignment. Killed the duplicate "Connected Desktop" surfaces — the relay is now the single source of truth for devices and the local gateway is explicitly flagged as advanced/optional.
- Mobile. Every page is single-column on narrow viewports, pill inputs wrap cleanly, hero drops to stacked layout, toolbar icons stay tappable (min 32px hit targets), horizontal overflow killed with
overflow-x-hiddenat every shell. - i18n. 100+ new keys across all 10 locales (en / ko / ja / zh / es / fr / de / pt / ru / ar). Key parity check passes at 97–100% per locale.
UX polish pass
- Consistent semantic tones across hero stat blocks. Warning stats were mixing
text-foreground,text-yellow-*, andtext-amber-*across Feed, Activities, and Logs — every page now uses the samewarn=amber / danger=destructivepair, so a 2 next to "Warnings" reads the same wherever it appears. - Feed rows stop auto-archiving on a misclick. The whole row used to be an archive button with a stealth time→action crossfade on hover; now the row is a passive list item and an explicit "Archive" hover-only pill handles the action. Unread dot uses
bg-primaryinstead of pure foreground so it matches the brand. - Refresh / Auto collapsed into one control on Activities. The separate Refresh button next to the "Auto" pill was redundant — now the toolbar shows a live-pulse dot when auto-refresh is on (click to pause) and a Refresh icon when it's off. State change is explicit, not dual.
- Activity hero totals match what the list shows. Hero used
dbEventswhile the list renderedmergedEvents(db + live), so in-flight live events were invisible to the tally and the type chip tallies. Both now derive from the same unfiltered merged stream. - Logs source filter comes out of hiding. The source chip row used to live behind a separate Filter icon toggle — only discoverable via curiosity. It's inline now, rendered whenever the server reports sources. One less click, one less icon.
- Schedules filter rail simplified. Status is a segmented pill (the axis users actually filter on); Range / Type / Sort / Schedule-picker drop to auto-width dropdowns. Triple "shown of loaded of total" counter replaced with a single "N of M" that only shows when a filter is active.
- Destructive actions gate on confirm. Schedules detail dialog and row dropdown no longer delete on a single click — a confirm prompt names the schedule so users see what they're about to lose.
- Gateway audit rows stop showing a Clock icon next to every timestamp. Redundant with the time on the right side of the row. Dead
relayConnected/tunnelConnectedstate and an unusedWifiimport removed. - Schedules day-group labels respect locale. "Today / Yesterday / This week" were hardcoded English; now they go through
chat.today / yesterday / this_weekso they follow the UI language. - Overview "Top tools" aligned to 24h window. Hero counter is 24h but the top-tools list was pulling lifetime stats labeled "Most used" — both are now 24h so the page tells a single consistent story.
Workspace on mobile actually scrolls now
- Mobile Workspace stopped clipping the editor at ~50% of the viewport. The previous layout cut the internal
h-fullandoverflow-hiddenat the md breakpoint so the page could rubber-band on iOS, but the side-effect was that the frontmatter + editor textarea got pushed off-screen below the sidebar header. The page now clamps to100dvh - 2.75rem(app header) on every breakpoint and manages scroll internally — the tree scrolls inside the sidebar, the textarea scrolls inside the editor, and mobile gets the same full-viewport behavior as desktop.
Demo preview caught up with the Usage rename
- Demo/preview mode's "Connect your desktop to use Overview" card now says "Usage" (plus a fresh one-line description about token burn / cost / model breakdown). The rename in v0.2.32 only covered the live app; the preview copy still referred to the page by its old name. Updated across all ten locales.
Landing page redesign
- Demo sidebar now matches the real app exactly. Icons corrected to match navigation.ts (Plug for Integrations, Clock for Schedules, Activity for Feed, Settings gear, Users for Agents, BarChart3 for Usage). Every sidebar item is now interactive with dedicated mock content panels for Usage, Schedules, Feed, and Settings.
- Foxl blue brand color system. Added
--brandCSS variable (desaturated blue from the Foxl logo) with light/dark mode variants. Applied as a gradient accent on the hero headline and subtle radial glows behind the hero and CTA sections. - Real provider icons in demo. The integrations grid now shows actual Microsoft, Slack, GitHub, Google Workspace, Notion, and Obsidian icons instead of placeholder letters.
- Logo in demo uses
logo-title.svgwith correct size and spacing matching the actual app sidebar.
New tray icon
- macOS menu bar icon redesigned with clean inner line separation. The new 2-piece SVG clearly distinguishes the left wing from the right body with a visible gap. Sized to 80% of the tray canvas for proper macOS menu bar breathing room.
"Overview" is now "Usage"
- The sidebar, page title, breadcrumb, and every localized label switched from "Overview" to "Usage" across all ten translations (en, ko, ja, zh, es, fr, de, pt, ru, ar). The page was never really an overview - it was the place users went to see token burn, cost, and model usage. The new name says that out loud. The internal route key stays
overviewso existing bookmarks, localStorage entries, and deep-links keep working.
Agents page editorial redesign
- Agents leads with a headline number instead of admin chrome. The old header stacked a small
h2 Agent team+ subtitle + a row of badges (1 active / 2 done) that all competed for attention. The page now opens with one large tabular number for running agents ("2 agents running") plus an inline three-stat rail (Active / Completed / Failed), same uppercase-tracked labels as the Usage dashboard. - Run bar became a single pill input. Goal field is now a rounded-full search-style input with a leading Send glyph, Run sits as the primary action, and Stop-all + Pixel-office toggle collapse into rounded-full secondaries. No more cramped
h-8row stuck between the status badges and the list. - Session history dropped the per-row Card chrome and rebuilt as a
<ul>withdivide-yseparators and uppercase date group labels ("ACTIVE / TODAY / THIS WEEK"). Status filters light up withbg-mutedinstead of swapping badge variants on every click. iter / tools / duration / error counts usetabular-numseverywhere so the row doesn't jitter as numbers tick up. - Consistent gutters. Agents now uses
px-4 sm:px-8 max-w-6xl mx-auto, matching Usage / Settings / Skills / Integrations so mobile stops reading edge-to-edge. - Every hardcoded English string moved to i18n (
agents.starting,agents.agent_running_singular,agents.no_sessions,agents.err, etc.) and the translation table got filled out across all ten locales.
Workspace gets the editorial treatment
- Workspace page dropped the Card wrappers and now reads as one continuous surface: file tree on the left separated by a single border, editor on the right. Header uses the same uppercase-tracked label + tight title as Overview, file path renders in mono, and the save button sits inline. Frontmatter pills switched to compact
key valuebadges. - Workspace action icons moved to the app header, matching Chat's pattern. Search / Open folder / Refresh now sit in the top header bar on the right, so the sidebar owns a single editorial label row instead of two competing action strips. The empty-state panel lost its large folder glyph in favor of a tiny uppercase
WORKSPACEchip plus the hint copy, and the editor pane became edge-to-edge without border/ring chrome so it reads like a real Linear/Vercel writing surface. - Workspace tree now shows a spinner during the initial fetch instead of two bare border lines with a void between them. The spinner only fires before
treeLoadedflips true, so manual refreshes don't flash it over a populated tree.
Overview loads faster
- Dashboard query does fewer scans. The
/api/dashboardendpoint was running seven separateSELECT COUNT(*)statements acrossconversationsandmessages, most of which had no usable index for theirWHERE created_at >= ?/WHERE role = ?clauses. Replaced the per-metric counts with two CASE-sum aggregates (one per table) and addedidx_messages_created,idx_messages_role_created,idx_conversations_created, andidx_tool_history_name. On a 12K-message / 365MB database the overview request dropped from a visible pause to effectively instant. - Overview caches its last render. Navigating away and back used to show a spinner for every round trip. The page now reads from
sessionStorageon mount and refreshes silently in the background, so subsequent visits are immediate. - Removed the "Uptime 15m" badge from the hero row - it was visual noise next to the token total.
- Usage Explorer range chips no longer clip off the left edge.
Overview + Usage Explorer redesign
- Overview got a proper dashboard instead of four identical admin cards. The page now leads with a hero row - last-24h tokens as one large number plus cost, a live uptime dot, and a "View Usage Explorer" chevron - and demotes conversations / messages / tool calls into a compact three-column strip next to it. Activity, Model usage, and Top Tools became bare typographic sections separated by horizontal dividers; the bar chart and list rows have more room to breathe without card chrome eating 48px on every side.
- Usage Explorer uses the same editorial layout. Breadcrumb + refresh on one row, chip-style range picker under it, 2x2 summary stats on mobile and 4-col on desktop, per-model rows with an inline progress bar and a 4-up breakdown (input / output / cache write / cache read) that stays on 2 columns on small screens. Custom date range inputs now flex to full-width on mobile instead of spilling off-screen.
- Numbers use
tabular-numseverywhere so columns don't jitter when values change during live polling.
Custom tool env-vars editor now behaves like a real textarea
- Enter, Space, and blank lines work again in the MCP env-vars box. Editing an MCP tool's environment variables used to feel broken: pressing Enter did nothing, trailing spaces disappeared, and the cursor would jump every keystroke. Root cause: the textarea re-parsed the whole text on every keystroke, dropped the parsed map back into props, and the parent re-serialized it from scratch, which stripped newlines and whitespace. The editor now holds the raw text locally and only pushes the parsed map up, so typing feels normal. Comments (
# ...) are also preserved across saves.
Heartbeat can run on any schedule now
- Feed Generator frequency is no longer capped at 6 hours. Settings -> Feed Generator now shows preset chips (15m, 30m, 1h, 3h, 6h, 12h, Once a day) plus an "Other" option that takes hours + minutes directly, so you can pick 17h 30m, once every 2 hours, once a day, or anything up to 7 days. The preset chips replace the old single-select dropdown. Custom values are clamped to a minimum of 1 minute and a maximum of 7 days to keep the scheduler sane.
- "Once a day" and "12 hours" are now first-class presets. Users who just wanted a morning briefing had to fake it with
7am cronschedules; now they can leave the Feed Generator on Heartbeat and pick the interval that matches their actual cadence.
`exec` is back
execandprocessare now core tools again. Some users reported the agent refusing to run a simplegit statusand falling back to the heavier terminal PTY path every time. Root cause:execandprocesswere gated behind theexec-toolskill, and a singleenabled: falseline in a syncedSKILL.mdwas enough to silently delete both from the tool registry at startup. They are now unconditional built-ins alongsidecode_search,browser,browser_extension, andweb_fetch. Approval gating still applies. Only OAuth-backed integration tools (Outlook, Slack) remain skill-gated, since they genuinely require a connected account to work.
Critical hotfix (rebuild)
- Fixed: app would not start after install. A route-pattern change that shipped with the first v0.2.30 build used
/api/workspace/:filename(*)syntax, which is rejected by the Express 5 path parser at boot. Every launch crashed before the embedded server bound its port, so the app sat on a retry screen forever (Attempt 1, 2, 3…). The wildcard routes for workspace files and webhook triggers are back on the working*filename/*splatform. If you were stuck on the loading screen, updating to this build fixes it. - New startup screen. When the local server is still coming up or has crashed, the loading screen now shows phase, port, PID, the last server error line, a rolling log tail, and three buttons: Restart server, Open logs, Copy diagnostics. Replaces the previous "Attempt N…" spinner that gave no indication of what was wrong.
Faster search across all your conversations
- Cross-conversation search now loads as you scroll. Before, Cmd+Shift+F only ever returned 20 results - anything past that was silently dropped, which was especially painful on large archives. The sidebar and the mobile search overlay now fetch the next page automatically when you scroll near the bottom, no extra "Load more" tap needed, until there's nothing left to match.
More auto-update fixes
- Clicking "Install Now" on the macOS native notification no longer crashes the app. The notification's action handler was focusing the main window before applying the update, which raced with the server shutdown that runs during quitAndInstall. The install path now defers to the next tick and skips the focus step so the update applies cleanly.
Strands SDK 1.1.0
- Upgraded
@strands-agents/sdkfrom 1.0.0 to 1.1.0. Brings proactive context compression, interrupt support for human-in-the-loop workflows, per-tool timeouts, retry strategies with configurable backoff, and MCP server log surfacing. Agent behavior is unchanged for existing usage; the new features will roll out in follow-up versions.
Use Foxl in your language, on any screen
- Integrations, Skills, Channels, and Tools pages now speak your language. Every page under the Integrations tab - including the Skills library, Channels, System tools, and Custom tools - reads in all 10 supported languages instead of always showing English. Section headings, filter buttons, search boxes, connect dialogs, error messages, and OAuth permission lists are all translated. Settings page too.
- Mobile-friendly layout across the whole app. Pages used to overflow on phones - long tab rows cut off, filter buttons got squished, dialogs didn't fit the screen. Now the tab bar at the top scrolls sideways with a soft fade on the right edge (so you can tell there's more), filter pills scroll the same way instead of shrinking their text, dialogs fit inside narrow screens, and search + filter rows stack vertically when there isn't room side-by-side.
- Preview mode on app.foxl.ai now matches the real app pixel-for-pixel. When you sign in without a desktop running, the sidebar shows every page (Agents, Workspace, Schedules, Feed, Relay, etc.) instead of only Chat + Settings. Clicking any data page shows a simple "Install Foxl" panel. The Install button auto-detects Mac, Windows, or Linux and shows the matching platform button.
Notes for developers
- New i18n blocks in the locale files:
integrations,skills_page,channels_page,tools_page,settings_page,header_tabs. - Demo components live in
apps/web/src/components/demo/(ConnectDesktopProvider,DemoPage,DemoIntegrations) so preview surfaces stay separate from the real pages. - None of the preview-mode behavior runs in the Electron desktop build -
IS_RELAY_ONLYis compile-time false there.
Auto-update fixes
- Mac "Install Now" button actually works now. Clicking "Install Now" in the native macOS notification used to do nothing. Fixed.
- Stale "update ready" notification cleaned up after install. After Foxl auto-updated itself, the old "ready to install" row would hang around in the notification bell and the button did nothing when clicked. Now the notification disappears automatically once the update has been applied.
Onboarding polish
- Skill library count in onboarding replaced with "Extensible skill library". The skill count kept rotting (73 → 68 → 35) every time we trimmed the library. Removed the number so it stops going stale.
Small fixes
- Clicking "Manage tools & MCP servers" in the HUD now goes to the right tab. It used to land on an older Tools page without the top tabs visible.
- Workspace file rows click on Windows again. Left-clicking a file in the Workspace sidebar was broken on Windows; fixed.
Workspace: real folder tree
- Workspace sidebar is now a proper folder tree you can expand and collapse. Replaces the three fixed sections (Core / Daily Memory / Sessions). Folders sort before files, hidden files stay hidden, and the tree remembers what you had open when you come back. Non-markdown files open in Finder instead of the editor.
- Workspace remembers the last file you opened. Reopen Workspace and you land on the file you were editing, with the folders around it already expanded.
Cleaner System and Custom tools pages
- System and Custom tools pages redesigned to match Integrations and Skills. Centered title, pill-shaped search, and category-grouped rows instead of the old card grid. Built-in tools are now grouped by type (Memory, Files, Web, Browser, etc.) so the list reads at a glance.
- Tool preset is now a dropdown. The four big preset cards became a single compact dropdown next to the search bar.
Sidebar restructure
- Tools moved under the Integrations tab. "Built-in" and "Custom" tools now live as two tabs inside Integrations (next to Integrations, Skills, Channels) instead of having their own sidebar entry. URLs:
/systemand/custom. - Agents tab no longer shows sub-tabs. Agents used to have a Tools sub-tab next to it - that's gone.
Tray menu fixes
- Menu bar dropdown now shows Recent chats and 7-day usage in the packaged app. This worked in development but was silently blank in the installed version - fixed.
- Clicking the menu bar icon no longer pops the window open. It opens just the dropdown. Use "Open Foxl" in the dropdown to bring the window back.
- First click after launch no longer shows an empty menu while the recent-chats data is still loading.
Skills cleanup
- Removed the "Trigger" field. The
trigger: manual / schedule / webhookfield in skills never actually did anything (scheduling lives in the Schedules page, webhooks go through channels). Removed from the skill editor and from the UI.
app.foxl.ai URL fixes
- Cleaner URLs when you open Skills, Channels, System, or Custom directly. No more
#skills,#tools, etc. hanging off the URL. - Direct links to /agents, /integrations, /feed now work on app.foxl.ai. Before, refreshing on these pages would bounce you back to Chat.
Landing page demo mock updates
- Desktop and mobile mocks on foxl.ai match the current app. Updated sidebar groups, model selector label ("Opus 4.7 Thinking"), Integrations/Skills/Tools panels rebuilt.
- Subtle parallax tilt. The mocks lean toward your cursor when it's in the surrounding whitespace, snap flat when hovering over the mock itself.
v0.2.27 blog post
- Published the v0.2.27 release notes as a blog post: "Trimming the Sidebar, the Skill Library, and the Tray."
Misc
- Pricing and "Try it out" sections on the landing page now visually separate from the sections above and below.
- SEO metadata updated on app.foxl.ai and docs.foxl.ai for Claude Opus 4.7.
Integrations / Skills / Tools: cleaner separation
- Sidebar reordered: Integrations → Skills → Tools → Schedules. The flow now reads left-to-right: connect a service → what the agent can do → when it runs.
- Integrations page only shows real third-party services. Before, anything with a "requires" field showed up here - including local document processors (PDF, Excel, etc.) and developer tools (exec, git, browser). Those all moved to the Skills page. Integrations is now just the OAuth connectors (Microsoft 365, Slack) and external SaaS skills (Notion, GitHub, Google Workspace, Obsidian, Spotify, etc.).
- Git removed as a built-in tool. The dedicated git tool was redundant - the agent handles git fine through the shell (
exec) tool, which gives more flexibility for pipes and custom flags.
Integrations page polish
- Rotating hero at the top now shows skills too, not just OAuth connectors. Each row gets its own gradient and sample prompt (e.g. "Review my pull requests and flag regressions" for GitHub).
- Microsoft 365 and Slack open a proper connect dialog first. Before, clicking Connect immediately kicked you out to the browser. Now you see a preview with the permissions being requested (and Slack's workspace input) before the OAuth flow starts.
- Channel icons show up properly. Every channel row was showing two-letter initials instead of its real logo - fixed.
- Connected channels no longer show a redundant "+" button. Just Stop / Edit / Remove.
Slimmer skill library
- Skill library trimmed ~60%. Deleted 32 rarely-used skills (Apple Notes, 1Password, various specialty tools). 21 commonly-used skills (browser, code search, exec, Notion, GitHub, Google Workspace, Outlook, PDF, etc.) stay enabled by default. Everything else defaults to off so they don't clutter the agent's context.
Streamlined tray menu
- Menu bar dropdown trimmed to essentials. Clicking the Foxl icon in your menu bar now shows: 3 most recent chats (click to jump right to the conversation), 7-day usage summary, then New Chat / Open Foxl / Quit Foxl. Removed rarely-used toggles (Stop Agent, Dark Mode, Always on Top, etc.).
Pricing restored
- Pricing section back on foxl.ai. Pro ($20/mo) and Ultra ($200/mo) plans, Credit Top-up, and the upgrade card in Account are all visible again. The brief "billing disabled" freeze from April is over.
Credit progress in the sidebar
- Click your avatar to see your credit usage. Compact progress bar showing how much of your monthly credits you've used, with a Pro/Ultra tier badge. Bar turns amber above 80% and red above 95%. Free-tier users get an "Upgrade" shortcut.
Smaller fixes
- Logos and icons render at full opacity everywhere. Removed the various
opacity-50/opacity-60/opacity-70dimming on the Foxl logo, platform icons under the download buttons, and the landing page's channel logos. - Docs site refreshed. Model pages, FAQ, and credits pricing now reference Claude Opus 4.7 as the flagship.
- Landing page SEO refreshed. Updated titles, meta, and structured data to reflect the current product (Opus 4.7, Windows support, Pro/Ultra plans).
New Foxl app icon (macOS + Windows + extensions)
- New Apple-squircle brand mark. Replaced the orange polygonal fox with a dark Apple-style squircle (dark monochrome fox silhouette + blue crescent accent). Canvas/padding match the previous icon layout (100px margin inside 1024 canvas), so the app tile reads at the same size on the Dock and in Finder.
- Covered every surface:
icon.icns(macOS Dock),icon.ico(Windows NSIS + taskbar, multi-res 16/32/48/64/128/256),icon-256/512.png, Windows installerbuild/icons/*.png(16-512), Chrome extension toolbar icons (16/32/48/128), and the web PWAapple-touch-icon.png+icon-192/512.png. - macOS menubar tray now renders from a new
menubar.svg(background-removed wordmark) at 22x22 standard and 44x44 retina, sized at ~80% of canvas so the tray icon visually matches surrounding menu-bar glyphs instead of looking oversized. - Unified brand across favicons and sidebar logos. All favicon SVGs (
favicon.svg,favicon-home.svg), sidebar logo variants (logo-color/dark/light/title/tray.svg), web PNG favicons (favicon.png,favicon-16x16.png), and landing-page favicons now use the new transparent mark — web app, landing, and docs all share the same brand asset.
Message queue drains as a single turn
- Multiple queued messages now combine into one turn. When you piled up several messages while the assistant was streaming, the old drain logic pulled them off the queue one at a time and ran a separate end-to-end turn per message — so three quick "also", "and", "oh one more thing" messages meant the model answered your first message fully, then your second, then your third, each without seeing the others. Drain now takes the whole queue for the current conversation on each wake-up, joins the entries with a blank line, and sends them as a single user turn. Matches Claude Code's behavior where rapid follow-ups land as one message.
- Applied to all three chat hooks:
useStreamingChat(local),useRelayChat(relay-only / app.foxl.ai),useTunnelChat(remote tunnel).
Generated image context menu
- Copy Image and Save Image As now work. Right-clicking a generated image (either the inline thumbnail or the zoom modal) and choosing "Copy Image" or "Save Image As..." was silently failing before. Two root causes: (1) the renderer's
fetch('file://...')is blocked by the defaultwebSecurity: true, so the saved-to-disk path fallback rejected with a network error; (2) after our asyncfetch→createImageBitmap→canvas.toBlobround-trip, the browser had already dropped the user-activation window thatnavigator.clipboard.writerequires and the write rejected withSecurityError. Both paths now route through new Electron main-process IPC handlers (copy-image-from-path,copy-image-from-data-url,save-image-as) that usenativeImage.createFromPath+clipboard.writeImagefor copy anddialog.showSaveDialog+fs.copyFileSync/fs.writeFileSyncfor save. Browser-only builds still get anavigator.clipboard.write+ anchor-download fallback. - Removed "Copy Image Data URL". The menu item duplicated "Copy Image" for most users' mental model and produced huge base64 blobs when pasted into a normal text field.
- Explicit toast feedback. Copy and save outcomes now show a sonner toast on success/failure, so a silent
navigator.clipboard.writerejection can no longer look like "nothing happened".
Subagent recursion guard (the real one)
- Subagents can no longer spawn subagents. Previously fork children kept the full
sessions_*tool schema (for byte-identical prompt-cache prefixes) and relied on two guards to prevent recursion: aBeforeToolCallEventhook that was referenced in comments but never actually registered, and anisInForkChild()scan that looked at the main conversation agent's messages viagetParentMessagesFn/ a hardcoded'default'fallback — never the running child's own messages — so it always evaluated false. Net effect: a child that decided to "delegate search" could fan out another 5 children, each of which could fan out 5 more, etc. Tree depth was bounded only by the 6-concurrent-agent cap racing against new spawns. - New hard guard:
sessionsSpawn.callbacknow checksgetCurrentAgentId()(backed by a newAsyncLocalStoragecontext thatrunAgententers for each subagent) and rejects if the caller isn't the'default'main agent.runWithAgentId(agentId, () => runAgent(...))wraps the spawn site insubagent.ts. - Belt + suspenders: the
BeforeToolCallEventhook that comments claimed existed is now actually wired up instrands-agent.tsfor every subagent Agent instance (fork or not). It cancels anysessions_spawn / sessions_status / sessions_stop / sessions_messagecall at runtime, regardless of whether the tool schema was filtered out or left in for cache sharing. - Dead code removed:
isInForkChildimport and the two call sites that scanned the wrong message lists.parseForkResultwas imported but never used — also removed. - parentId lineage fix: spawned subagents now record the real spawner's
agentIdviagetCurrentAgentId()instead of a hardcoded'default', so long-running-watch messages and UI parent links point at the agent that actually asked for the work. (Recursion is blocked before this matters in practice, but the registry data is now correct instead of lying.) - System prompt guidance rewritten to match Claude Code's delegation model. The old "RESEARCH & WEB SEARCH" block told the agent to "Spawn 3-5 subagents in parallel with different search angles" and to poll with
exec sleep Nbetweensessions_statuscalls — which encouraged fan-out on every research query and wasted tool calls on sleeps the runtime doesn't need (results arrive automatically). Replaced with explicit delegate-vs-work-directly rules: delegate for multi-file changes / refactors / debugging / broad research / verification / context-flooding outputs, work directly for trivial ops. Brief each subagent as a cold teammate (goal + in/out scope + brevity). Launch independent subagents in a single turn, don't duplicate delegated work, don't poll, and explicitly: subagents cannot spawn further subagents. Matching update insystem-prompt.tsMulti-Agent System section.
Subagent result gathering matches Claude Code's Task-tool semantics
- Batch-complete gather. Before, every individual subagent completion notified the server to drain, so a fan-out of 5 parallel subagents could trigger 2–5 separate auto-continue passes on the same conversation — the parent would synthesize mid-batch, then get poked again with the stragglers.
onSubagentDoneinsubagent.tsnow counts peers that share the sameconversationIdandstatus === 'running', defers the drain notification until it's the last one reporting, and only then firesautoContinueFn. Results still land in the durable DB queue as they arrive (so no completion is lost if the lock is briefly held by a mid-flight drain), but the synthesis pass runs once per batch — matching Claude Code's Task-tool behavior where a batch of parallel sub-tasks returns as a singletool_resulton the parent's next turn. - Registry extended with
conversationId. Required to scope peer-running counts to the right conversation so two concurrent chats don't delay each other. - Consolidated message shape. The auto-continue prompt is now
"N subagents have returned. Synthesize their findings into a single response for the user. Do not delegate further; do not restate the mechanism."followed by labeled### Subagent i/N — <goal>sections separated by---, with failed ones carrying a[FAILED]tag. Previous boilerplate ("The following subagent results have arrived. Synthesize them into a clear, organized response...") encouraged the model to restate the mechanism back to the user. - Dead parser removed.
strands-agent.tshad a branch that rewrote the prompt if an injected message contained[SUBAGENT COMPLETED:/[SUBAGENT FAILED:, but those tokens haven't been produced anywhere since auto-continuation moved to the DB queue — the branch never fired. Dropped. - Tool + prompt docs synced.
sessions_spawntool description,strands-agent.tsAUTONOMOUS_AGENT_INSTRUCTIONS, andsystem-prompt.tsMulti-Agent System section now all describe the same batch-complete delivery shape instead of the old "[SUBAGENT COMPLETED] message arrives per agent" promise.
Notification popover cleanup
- Consolidated notification action buttons to a single "Open" per row. Previously each entry had a type-specific primary label ("Open Agent" / "View" / "Reply") plus a redundant "Dismiss" button; the action row could show "Open / Dismiss / Dismiss" when a notification was rehydrated with a stale secondary action. Now there's exactly one action button labeled "Open", and dismiss lives as a per-row X icon that appears on hover.
- Native macOS banner actions also collapsed to a single "Open" button. macOS's notification shell already provides a close affordance, so an explicit "Dismiss" button was duplicating the OS chrome.
Notifications: actions, deep-links, and a working Install Now
- Action buttons on every notification type. Native macOS banners now carry "Open Agent / View / Reply / Dismiss" for agent done/failed, schedule done/failed, chat/channel message, and feed events. Previously only the update-ready toast had actions; everything else rendered as an unactionable banner that did nothing when clicked.
- Notification body-click navigates to the event page AND deep-links to the specific resource. Clicking a "Schedule: daily-briefing" banner now opens
/schedules#{scheduleId}, an "Agent Done" banner opens/agents/{sessionId}, a Slack channel message opens/chat/{conversationId}, and a feed alert opens/activities. Before, body-click only focused the window and dropped the user on the default page. - Action button clicks carry meta through the full stack. Server broadcast → WS → renderer → Electron main → back to renderer — the originating event's
sessionId/conversationId/scheduleIdis preserved end-to-end so deep-link navigation works for both the body click AND the action button. - Install Now on the update-ready toast actually installs.
autoUpdater.quitAndInstallis now scheduled on the next tick (was racing with the notification dismiss on macOS, which is why the button looked like it did nothing), explicitly setsautoInstallOnAppQuit = trueas a belt, and falls back toapp.quit()if the native call throws. - Bell popover and native toast are consistent. The in-app bell popover shows the same action buttons as the OS toast, so the experience is uniform on Windows/Linux where native actions aren't available.
- Server-side meta added to every emission site.
subagent.tsnow attaches{agentId, sessionId, conversationId}to completion notifications.server.tsschedule emissions carry{scheduleId, scheduleName}. Channel message emissions carry{conversationId, channel, channelId}. Single-agent start/goal endpoints carry{sessionId: 'default'}.
Notifications Settings cleanup
- Settings test picker now goes through the real server broadcast path. The picker was using a
CustomEventshortcut that bypassed the WebSocket round-trip, which meant it could pass locally while production was broken. It now hitsPOST /api/test/notificationwhich broadcasts over WS the same way production agents do, so clicking "Send test" exercises the exact same pipeline as a real event. - Settings test scenarios now mirror actual server emissions. Added
channel_message_whatsappandchannel_message_webpickers (missing before). Droppedpet_expedition_return/pet_item_dropscenarios — no code ever emitted those types, so the pickers were dead UI. Also removed the matching pet toggles from the settings schema. - Test picker resolves deep-link IDs from the server. When you test an agent notification, the picker fetches your most recent agent session and uses its ID as
meta.sessionIdso the click actually lands on a concrete row instead of the bare list. Same for schedules and conversations.
Agents page
- Active agents now render in the list. The header "N active" badge read in-memory team state, but the list below it only showed DB-persisted sessions — so chat-driven runs and not-yet-persisted agents appeared in the badge count but were missing from the list entirely. Live members are now merged as synthetic rows in a pinned "Active" group at the top, and running sessions bypass the Today / 7 Days / 30 Days time filter so a long-running agent stays visible even when the selected window excludes its start time.
Tests
notifications-e2e.spec.tsgained 17 new tests: meta propagation (5), Settings scenarios mirror server emissions (11, one per real type), subagent completion shape (1, skipped when no provider configured).- New
notification-actions-ui-e2e.spec.tscovers the UI half: Settings picker hits the real endpoint, bell popover renders action buttons, each action's pushState observed via ahistory.pushStatespy for race-free deep-link verification.
Schedule timeout UI
- Schedule execution timeout is now visible and configurable in the create/edit/detail dialogs. Displayed in minutes (stored as seconds), default 10 min. Hidden for heartbeat type (managed by server).
Fixes
- Fenced code blocks without a language tag (triple backtick) were rendered as inline code, collapsing newlines into a single line. Now checks content for newlines to determine block vs inline rendering.
Strands SDK v1.0.0 + Express 5 upgrade
- Upgraded
@strands-agents/sdkfrom v1.0.0-rc.5 to stable v1.0.0. Concurrent tool execution enabled by default - the LLM decides when to run multiple tools in parallel within a single turn. - Added
BeforeInvocationEventandBeforeModelCallEventcancellation hooks (SDK v1.0.0 feature). Agent stop requests now prevent the next API call from firing, saving cost and reducing latency on user-initiated stops. - Refactored per-agent tool event routing from a global Map (
agentToolEventFns) to SDK-nativeinvocationState- eachagent.stream()call carries its own callback via the per-invocation state bag, eliminating shared mutable state. - Upgraded Express from v4.18 to v5.2 with
@types/expressv5. Migrated all wildcard route patterns to Express 5path-to-regexpsyntax (:param(*)to*param, unnamed*to*splat). Fixed wildcard param handling - Express 5 returns arrays for*captures, now joined with/for path reconstruction.
Fixes
- Workspace file read/write via nested paths (e.g.
memory/daily/2026-04-30.md) broken by Express 5 array params - fixed withArray.isArray()guard andjoin('/'). - Webhook trigger endpoint updated for Express 5 named wildcard (
*splat).
Tests
- Added
sdk-upgrade-e2e.spec.tswith 11 new Playwright tests covering Express 5 wildcard routes, chat stop/cancellation, server health, and provider/model endpoints.
Custom tools & MCP: servers now actually reach the agent
- Custom tools stored in the
custom_toolsDB (shell, http, file, script,mcp_local,mcp_remote) were never wrapped as Strands tools — the LLM literally couldn't see or call them. AddedbuildStrandsToolsFromCustom()inserver/tools/custom-tools.tsthat wraps every enabled custom tool as a proper Strandstool()and appended them togetEnabledTools(). Custom tools bypass the profile system — if the user enabled them, the agent gets them. - MCP tools now default to expand-all mode: one MCP custom tool exposes every tool the remote server advertises. Each remote tool becomes its own Strands tool named
<customName>__<remoteTool>(e.g.server_time__get_current_time,server_time__convert_time). Removed the stalemcpToolNamesingle-target plumbing that was silently misrouting calls. - JSON Schema → zod converter ports the MCP server's
inputSchema(types,required,enum,description) into the Strands tool. Fixes the bug where the agent saw an empty{}schema and had to guess argument names / required-ness. executeMcpLocal/executeMcpRemotenow honor a per-call__mcpToolNameoverride injected by the wrapper, so expand-all routes correctly to the right remote tool on every call.
Connection status tracking & auto-probe
- New
McpStatuscache +refreshMcpStatus(def)/refreshAllMcpStatuses()helpers. States:unknown | connecting | connected | error | disabled, withtoolCount,tools[] (name/description/inputSchema), anderror. - Probe is triggered automatically on: server boot (for every MCP tool in the DB), custom-tool create, update (including enable toggle), delete (cache pruned for missing ids). No manual step needed.
- New endpoints:
GET /api/tools/custom/status(id → status map) andPOST /api/tools/custom/:id/refresh(force re-check). Route ordering fixed so/statusmatches before the/:idcatch-all. inputSchemapreserved in the cached status payload so the tool builder can forward the real JSON Schema (includingrequired) to the agent. Without this the model couldn't tell thattimezonewas required onmcp-server-time.
HUD redesign: flat, obviously-clickable, with MCP status
- ElectronTitlebar HUD rebuilt in the shadcn/ui design language: flat borderless pill with
text-muted-foreground → text-foregroundhover, chevron down indicator, status dot on the left, optional MCP badge on the right. No more raised button edges. - Popover expanded to 320px with clear sections separated by
Separator: status header, metrics grid (Agents / Tool calls / Iteration), context usage bar, MCP servers list (per-server status dot + tool count or truncated error), recent tool calls, and a clickable footer row that navigates to the Tools page (amber alert icon when any MCP errored). - Trigger auto-fetches MCP statuses on mount and polls every 5s while the popover is open — the number-of-connected badge updates without a manual refresh.
Tools page: edit existing custom tools
- Each custom tool row is now fully clickable (hover background, keyboard focus) and opens an Edit dialog that pre-populates every field (name, description, type, config, parameters). For
mcp_localthe space-joinedcommand/argsfield is reconstructed so the UI round-trips cleanly. - Dedicated Pencil edit button next to Play / Delete / Refresh. Dialog header and save-button label switch between Create / Edit mode automatically. Saving an edited MCP tool triggers a re-probe immediately.
- Non-MCP rows keep the same inline controls; action buttons stop click propagation so they don't accidentally open the edit dialog.
Misc
image-viewer.tsxtightened to match the rest of the tool-flip UI (generated / viewed images render with consistent collapsed / expanded states and zoom controls).- Landing site: FAQ additions, About / Legal / Blog copy edits, v0.2.19 blog post polish, new
humans.txt, updatediphone-frame.svg, sitemap regen. site/worker.jsupdated alongside new static asset routes (.well-known/, blog hero images).- Docs regenerated from latest MDX (
docs/public/docs/{models,providers,tools}.md+llms-full.txt+ sitemap) — covers thegpt-image-2+ OpenAI OAuth changes shipped in v0.2.19. foxl/scripts/debug-codex.mjs,debug-image-tool.mjskept in-tree for future Codex / image-gen wire triage.
`generate_image` tool: gpt-image-2 through ChatGPT subscription
- New built-in
generate_imagetool that produces or edits raster images viagpt-image-2without an OpenAI API key. Powered entirely by the user's existing ChatGPT Plus/Pro OAuth session (~/.codex/auth.json/CODEX_HOME/CHATGPT_LOCAL_HOME); no per-image billing outside the $20/month subscription quota. The tool posts tochatgpt.com/backend-api/codex/responseswithmodel: "gpt-5.4"carryingtools: [{ type: "image_generation", model: "gpt-image-2" }]— the same shape the official Codex CLI uses for its built-inimage_gentool. - Tool is gated to accounts with ChatGPT OAuth: only registered when the credential file is discoverable at module load, skipped otherwise so non-OAuth users don't see an unusable affordance.
- Accepts multi-image input for edits and composition.
inputImages: string[](max 10 paths) switches gpt-image-2 from text-to-image into edit / compose mode — "이 사진 만화풍으로 바꿔", "이 두 이미지 합쳐줘", reference-guided generation, etc. - Token-efficient wire shape: only file paths cross the model's tool-call args, never base64. The tool reads each path from disk inside the server process and encodes it as
input_imagedata URIs directly on the Codex request. Return payload to the model is trimmed toSaved to: <path>+ a sharp-resized 768px JPEG preview (~100KB) — the full-resolution PNG stays on disk atoutPath. - Attached chat images auto-persist to
data/workspace/attachments/chat-<timestamp>-<rand>.<ext>and their absolute paths are injected into the user's text block as[Attached image paths: "..."]. The model therefore always knows the exact disk path to pass togenerate_image({ inputImages: [...] }), closing the loop from drag-and-drop → edit. - Saved outputs live at
data/workspace/generated/generated-<YYYY-MM-DD>-<prompt-slug>-<suffix>.png. Workspace root (SOUL/USER/MEMORY/TOOLS/AGENTS/HEARTBEAT/BOOTSTRAP.md) stays uncluttered; every generated image carries its date and a readable slug derived from the first six words of the prompt. data/workspace/generated/excluded from workspace file listing automatically (subfolder convention), accessible via the Open Folder button now on the Workspace page header.- ChatGPT-style UI: generated images render expanded by default directly in the chat stream (not tucked inside a collapsible tool flip), with the standard Task chevron for collapse and consistent sizing with other tool calls.
view_imagekeeps its original collapsed-by-default compact UX. The full-resolution preview modal offers click-to-zoom (25%–400%) and an Open Folder button that reveals the saved PNG in Finder / Explorer / xdg-open. - Tool-profile wiring: added
mediacategory (['generate_image']) totool-profiles.tsand included it indefault,standard, andfullprofiles.minimalomits it to preserve its token budget.
OpenAI OAuth: catalog + model availability
- Added
openai-oauth/gpt-5.5to the catalog (1M context, reasoning + streaming). Verified serving on the ChatGPT OAuth endpoint via live probe.gpt-5.5-codex,gpt-5.5-mini,gpt-5.5-proare all gated to API-key accounts (400 Bad Request: "not supported when using Codex with a ChatGPT account") and therefore excluded. - Final catalog:
gpt-5.5(1M),gpt-5.4(1M),gpt-5.4-mini(400K),gpt-5.3-codex(400K). - Removed
openai-oauth/gpt-5.4-codexandopenai-oauth/gpt-image-2as top-level language-model entries. The ChatGPT OAuth backend returns400 Bad Request: "The 'gpt-5.4-codex' model is not supported when using Codex with a ChatGPT account"(same forgpt-image-2) despite the OpenAI Developer Community announcement.gpt-image-2is still reachable, but through the newgenerate_imagetool above. - Wired up
imageModel()/image()oncreateOpenAIOAuthregardless, backed by@ai-sdk/openai/internalOpenAIImageModelreused through the same OAuth fetch. Ready to flip on the moment OpenAI enables raw image models on the ChatGPT OAuth endpoint.
Fix: `openai-oauth/gpt-5.4` was silently routed to gpt-4.1
shared/model-resolver.tshad a staleNAMESPACE_ALIASESentry mappingopenai-oauth/gpt-5.4→gpt-4.1(andopenai-oauth/gpt-5.4-codex→gpt-4.1). Any time the user selected the OAuth GPT-5.4,setModel → toCanonicalIdresolved the alias and storedgpt-4.1as the active model — which Codex then rejected with a bare400 Bad Requestbecause ChatGPT OAuth accounts don't servegpt-4.1either. Removed both aliases so the transport-prefixed id passes through untouched. Verified end-to-end:/api/providers/selectnow persistsopenai-oauth/gpt-5.4verbatim and the follow-up/api/chat/streamreturns reasoning + text deltas as expected.
Debug: surface real Codex upstream errors behind `FOXL_CODEX_DEBUG=1`
- Added an opt-in
FOXL_CODEX_DEBUG=1env flag inopenai-oauth/core/transport.tsthat clones and logs the response body + request body on any non-2xx fromchatgpt.com/backend-api/codex/*. Strands wraps upstream errors as"Language model stream error: Bad Request", which discards the real cause; this flag is the single switch that reveals it. Off by default (no logging overhead in production).
UX: Workspace page "Open Folder" button
- New folder-open icon button in the Workspace header (next to Search and Refresh) that opens
data/workspace/in Finder on macOS, Explorer on Windows, andxdg-openon Linux. Electron shell path viawindow.pilot.openPath; non-Electron falls back to newPOST /api/system/openendpoint.
Skills: remove duplicate image-gen skills from foxl-ai/skills
- Removed
openai-image-gen(OPENAI_API_KEY + Python script path) andnano-banana-pro(GEMINI_API_KEY + uv + Python) from the skills repo. Both are superseded by the nativegenerate_imagetool above. One canonical image path across the product.
Dependencies: Strands SDK → v1.0.0-rc.5
- Bumped
@strands-agents/sdkfrom^1.0.0-rc.3to^1.0.0-rc.5. Picks up: mid-execution cancellation (agent.cancel()/cancellationSignalviaAbortSignal.timeout()), agent-as-tool (agent.asTool()), multi-agent session persistence, Bedrock thinking+forced tool_choice conflict fix, Bedrock context window overflow detection matching the Python SDK, invocation lock leak fix on consumer stream break, AgentSkillsPlugin → AgentSkills rename,contextWindowLimitadded toBaseModelConfig, and OTEL JS SDK v2 upgrade. No call-site changes needed in Foxl.
Frontend hygiene: zero web typecheck errors
- Fixed 19 pre-existing TypeScript errors across the web codebase so
tsc --noEmitinapps/webis now green. Bumpedtarget/libfrom ES2020 → ES2023 and addedvite/clientto thetypesarray (unlocksArray.prototype.findLastIndex, stricterArrayBuffertyping, andimport.meta.envtyping). Deleted unusedFloatingLines.tsx(referenced a missingthreedep). Tightened cast sites inuseWebSocket.ts(WebSocketMessagehad an[key: string]: unknownindex signature that made field narrowing impossible),tunnel-crypto.ts(BufferSource / Uint8Array overloads), andsearch-bar.tsx(RefObject nullability). None of these were blocking the Vite build, but keepingtscgreen is a non-negotiable guardrail for future refactors.
Claude Code (OAuth): Opus 4.7 gets its 1M context back
- Opus 4.7 on the Claude Code OAuth transport now reports its true 1M context window. Since Opus 4.7 shipped in v0.2.11,
claude-code/opushad been hard-coded to 200K because the CC OAuth catalog predated the 1M rollout. The result:/contextusage percentages showed ~5× inflated values and auto-compaction kicked in five times earlier than it should have, effectively capping Opus 4.7 sessions at 200K. A live probe againstapi.anthropic.comwith a 320K-token prompt confirmed Opus 4.7 on the subscription pool accepts the full input atservice_tier: standardwith nocontext-1m-*beta header — 1M is the native default, the beta flag is only for back-compat with older models. - Sonnet 4.6 on the same CC OAuth transport stays at 200K. A parallel probe (identical shape, only
modelswapped) returned HTTP 429 "Extra usage is required for long context requests" — Anthropic routes Sonnet's long-context requests into the pay-as-you-go pool instead of the subscription pool. Haiku 4.5 is likewise kept at 200K. - Added
resolveEffectiveContextWindow(modelId)inshared/model-resolver.tsso UI context gauges (useStreamingChat.ts) and server-side auto-compaction (server/agent/compaction.ts) both see the transport-adjusted ceiling rather than blindly trusting the catalog entry. Catalog entries keep their paper-spec numbers (Sonnet 4.6 = 1M via Bedrock/BYOK) and the helper narrows them only for transports that impose a cap.
Claude Code (OAuth) model ids: full Anthropic name after the prefix
- The CC OAuth catalog now exposes
claude-code/claude-opus-4-7andclaude-code/claude-sonnet-4-6as the canonical ids. The legacy short-form ids (claude-code/opus,claude-code/sonnet,claude-code/haiku) are preserved as aliases inCLAUDE_CODE_MODEL_ALIASESand in the sharedNAMESPACE_ALIASESmap, so previously persisted user selections (localStoragefoxl-selected-model, relay/api/providers/selectstate) keep resolving correctly. Verified end-to-end with a live probe — all four id shapes (new/old × Opus/Sonnet) route to the expected Anthropic model withservice_tier: standard.
Fix: model switch was a no-op on foxl.ai provider
- Switching model mid-conversation on the foxl.ai provider (e.g. Kimi K2.5 → Opus 4.7) kept running the previous model because
conversationAgentscaches StrandsAgentinstances andAgentis constructed with a baked-in model + provider. The POST/api/chat/streamhandler already calledsetModel(newModel)from the request body, but the cached agent ignored it. Added a parallelconversationAgentModelsmap that records each cached agent's construction-time model, andhandleStreamingChat()now discards the cached agent as soon ascachedModel !== config.model. All 16 existingconversationAgents.delete(…)/.clear()sites were mirrored so the maps never drift.
Relay: prompt caching on the Bedrock forward path
relay/src/proxy.tsnow injects block-levelcache_control: { type: 'ephemeral' }breakpoints on every Anthropic-format/v1/messagesbody before it hands off to Bedrock InvokeModel. The tool list, the system prompt, and the last user turn each get one breakpoint (up to Bedrock's 4-breakpoint limit) sotools → system → messagesprefix caching kicks in automatically across turns. Bodies that already carry explicitcache_controlmarkers are left alone. Verified with a dedicated unit-test suite (relay/tests/09-cache-breakpoints.test.ts, 7 cases covering string/array system shapes, tool lists, multi-turn conversations, andtool_resultblocks).- Strands'
AnthropicModelconfig only exposes acacheConfigthatBedrockModelhonors —AnthropicModelitself doesn't auto-inject cache points, so the relay-side injection is where caching on the foxl relay path actually turns on. Bedrock InvokeModel does not honor the top-level automatic-caching shorthand described in Anthropic's API docs (that's Claude API + Azure AI Foundry only), which is why block-level markers are the right place to hook.
Site: foxl.ai gets a real dark mode
- Added a
.darkpalette tosite/landing/src/index.css(background/foreground/card/muted/border/ring/accent all defined) and an inline pre-paint script inindex.htmlthat applies the saved or system theme before first paint so there's no FOUC on hard refresh. A newThemeTogglecomponent (site/landing/src/components/landing/ThemeToggle.tsx) sits in the Navbar next to the Download button on desktop and next to the hamburger on mobile, persisting the user's choice underlocalStorage['foxl-theme']and falling back toprefers-color-schemewhen unset. - Swept landing components for hardcoded light palette:
Security.tsxcards switched frombg-whiteto the semanticbg-card, and the LegalPage changelog version badge switched frombg-zinc-200 text-zinc-700tobg-muted text-muted-foreground. The only remaining hardcoded colors are intentional (the iPhone notch inMobileDemo.tsxstaysbg-black, the toggle knob inDesktopDemo.tsxstaysbg-white).
Revert: Claude Code (OAuth) compat layer is back on
- v0.2.17 disabled the Claude Code (OAuth) compat-mode tool rewriter based on raw
/v1/messagesprobes that appeared to show Anthropic's Pro/Max subscription pool now accepting Foxl-native tool names. In production users immediately hit "Language model stream error: You're out of extra usage" — the raw probes did not reproduce the shape that Strands + Vercel AI SDK put on the wire in a real agent loop, so the subscription pool kept rejecting the request and Anthropic fell back to pay-as-you-go billing. - Reverted:
strands-agent.tsandserver/api/server.tscallbuildCompatTools()again forproviderType === 'claude-code', the tool-adapter banner is back to its v0.2.16 wording, andclaude-code/haikuis excluded from the model catalog again (it was re-added in v0.2.17 based on the same faulty probes). - Compat mode returns to being the supported Claude Code (OAuth) path. Investigation to capture a real failing body and validate an end-to-end bypass is tracked in TODO.md; the probe scripts stay on disk as regression guards for the compat path.
Auto-update notification actions
- Clicking "Install Now" (or the notification body) on the "Foxl update ready" toast now actually installs the update and restarts the app. Two separate notifications used to race on download completion: a renderer-side rich notification with action buttons and an actionless main-process Notification. The main-process toast usually won, its
on('click')handler only focused the window, and the user saw "Install Now" do nothing. Removed the duplicate toast so the renderer's action-bearing notification is the only one the user sees. show-notificationnative-toast click handler now treatstype: 'app_update'as an install affordance rather than a focus-only click, so clicking the toast body (on any platform) installs the update the same way the explicit "Install Now" action does.
Claude Code (OAuth): full native tool registry
- Claude Code (OAuth) now sees Foxl's full native tool registry (memory, workspace memory, subagents, schedule, channel send, browser, view_image, etc.) — everything that was dropped by v0.2.16's compat-mode tool adapter. Anthropic's Claude Pro/Max subscription pool no longer gates on CC-shaped tool names, so the Bash/Read/Grep/WebFetch shim layer is bypassed in
strands-agent.tsandserver/api/server.ts. Proven by two new probe scripts that route every combination (Opus/Sonnet/Haiku × native tool lists) throughapi.anthropic.com/v1/messagesand verifyusage.service_tier === 'standard'on both the initial request and a fulltool_use/tool_resultround-trip. server/providers/claude-code-oauth/compat/tool-adapter.tsis kept on disk (no longer imported) so the rewriting strategy is instantly recoverable if Anthropic re-enables the gate.compat/message-rewriter.tsis still wired up — the subscription pool remains sensitive to the SYSTEM block shape, so Foxl still relocates persona/context fromsystemto a<foxl-context>prefix on the first user turn.- Re-added
claude-code/haiku(Haiku 4.5) to the Claude Code (OAuth) catalog. The 2026-04-22 probe sweep showed Haiku now routes cleanly across the full native tool set — the instability that caused it to be excluded in v0.2.16 no longer reproduces.
Claude Code (OAuth) probe scripts
scripts/test-claude-code-oauth-compat-probe.mjs— 3×3 matrix (Opus/Sonnet/Haiku × no-tools/single-tool/full-CC-set) verifying subscription-pool routing against the CC-shape rewrite.scripts/test-claude-code-oauth-native-tools-probe.mjs— 3×3 matrix using Foxl-native tool names (exec, file_read, code_search, web_fetch, memory_save, sessions_spawn) to prove the CC-shape rewrite is no longer required.scripts/test-claude-code-oauth-roundtrip-probe.mjs— drives a full tool_use → tool_result → follow-up turn with Foxl-native names and asserts subscription routing on both turns.scripts/test-claude-code-oauth-claims-probe.mjs— empirically confirms the "OpenClaw re-allowed" announcement's claims against the live API:service_tier: autoandstandard_onlywork,cache_controlis a no-op over OAuth,context-1m-2025-08-07beta is rejected onsk-ant-oat-*tokens,claude -pCLI still runs, adaptive thinking plumbing returnsthinkingcontent blocks.
Notification click/action routing
- Notification bell items now actually navigate to the relevant page when clicked. Previously, any notification that carried action buttons (Install / Later on "update ready", or anything with explicit actions) swallowed the body click and stayed on the current page. Agent-complete, feed, schedule, and channel notifications now route to their owning page on click and close the popover afterwards.
- Action buttons whose id is not a special case (
install-update,dismiss-update) now fall back to navigating to the notification's navTarget, so "Open Agents" / "View Feed" style buttons in test notifications and future richer actions work without extra wiring. - Native OS notification clicks now route for
agent_complete,agent_error,feed,feed_urgent, andheartbeattypes as well (previously only chat / schedules were handled; everything else just focused the window). - Deep-link resolution extended: navigating to
chatwithmeta.conversationIdnow callschat.loadConversationand updates the URL; navigating toschedulesoractivitieswithmeta.scheduleId/meta.feedIdwrites the id into the URL hash so the target page can scroll or expand the relevant row.
Settings: interactive Test notifications
- The Test card in Settings > Notifications is now a working end-to-end test instead of a preview-only helper. Picking a scenario and hitting Send inserts a live rich notification (with an "Open" action) directly into the bell popover via a CustomEvent bridge, so you can verify click and action routing without waiting for a real event.
- Each scenario resolves a real deep-link target: agent scenarios fetch the latest
/api/agent-sessionsand route clicks to that session's detail page; schedule scenarios use the first/api/schedulesrow; channel_message scenarios use the most recent conversation. When no rows exist, the placeholder id still exercises the deep-link plumbing. - New "Also fire native OS notification" checkbox now calls
pilot.showNotificationdirectly from the renderer so the actions array reaches the Electron main process and the native banner renders the same "Open" button. Previously the test went through/api/test/notificationwhich stripped actions and also inserted a duplicate row in the bell. - Dismiss action removed from the test notifications — macOS notifications have a built-in close and the bell popover closes on outside click, so a second dismiss button was redundant. The action label is now just "Open" across all scenarios, localized in all 10 locales.
Claude Code (OAuth) compatibility mode
- Re-enabled the "Claude Code (OAuth)" provider with Opus 4.7 and Sonnet 4.6 entries. Haiku stays excluded because even the official CLI tends to 429 once the tool list grows past a few entries on Haiku.
- Added a transport-level rewrite layer (
server/providers/claude-code-oauth/compat/) that conforms Foxl's agent-shape requests to what Anthropic's Pro/Max subscription pool actually routes. Two transforms run on every/v1/messagescall: message-rewriter.tsmoves Foxl's persona/workspace context out ofsystemand onto a<foxl-context>prefix on the first user turn, leavingsystemas the preamble-only single block the pool gates on.tool-adapter.tswraps each supported Foxl tool in an official-CC-named shim (exec->Bash,file_read->Read,code_search->Grep,web_fetch->WebFetch). Wrappers translate the CC-shape input back to Foxl-shape before delegating to the underlying Foxl tool, so Strands' agent loop keeps running existing callbacks unchanged.- Foxl-exclusive tools with no CC counterpart (memory, workspace memory, subagents, schedules, channel send, view image, browser extension, ...) are dropped from the tool list in compat mode - the model never sees them and cannot call them. First-time selection surfaces an AlertDialog explaining the trade-off (translated into all 10 supported locales).
- Docs: restored the Claude Code (OAuth) sections in
docs/content/docs/models.mdxandproviders.mdxwith compat-mode caveats.
Empty-chat layout
- When the terminal pane is open, the empty-state greeting + input no longer try to vertically center themselves over the terminal. The content now sits top-aligned above the terminal so both are fully visible.
Claude Code (OAuth) temporarily disabled
- Hidden the "Claude Code (OAuth)" provider and its Opus/Sonnet/Haiku entries from the model picker, settings page, and
/api/providersresponse. The transport, credential loader, and Strands wiring are kept in the codebase so this can be re-enabled later. - Why: Anthropic's Claude Pro/Max subscription pool rejects Foxl's agent-shape requests. Raw probes against
api.anthropic.comusing the real OAuth token showed that a 1-line preamble with no tools routes to the subscription pool (200 +service_tier: standard), but adding Foxl's 22-tool registry or a 23KB persona system prompt causes Anthropic to silently re-route to the pay-as-you-go API pool. Users on Pro/Max were burning "Extra usage" without realizing the calls had fallen off their subscription. - Investigation also aligned the OAuth transport with the official Claude Code CLI (
anthropic-beta: oauth-2025-04-20,claude-code-20250219,user-agent: claude-cli/...,x-app: cli, single merged system text block,output_configstripped, Haiku usesbudget_tokensthinking instead ofadaptive). Those fixes stay in the tree for when Anthropic ships a subscription-compatible agent surface. - Workaround for users: use Anthropic API key (BYOK) or the foxl.ai relay for Claude access.
Empty-chat redesign (desktop + web app)
- New chats open with a Codex/Claude-style centered layout: a single "What should we work on?" greeting directly above the input box, vertically centered in the viewport. As soon as the first message is sent the input snaps back to the normal bottom dock.
- Dropped the six hardcoded prompt cards (Web Search / File / Debug / Code / Terminal / Analysis). They were generic enough to be noise. Pill-style AI suggestions still render directly under the input when
/api/suggestions/generatereturns any. - Foxl logo no longer prints in the empty state.
- Mobile keeps the earlier v0.2.11 layout (top-aligned logo + greeting + bottom-docked input) so the on-screen keyboard does not fight the centered overlay.
Fixes
- Suggestions fetch used an
AbortControllerwhose cleanup fired during React StrictMode's double-invoke effect, cancelling the inflight request so pills never appeared in dev. Switched to a cancelled-flag pattern - the request completes and the cache is warmed, results are just ignored if the component unmounted first. - app.foxl.ai (relay-only mode) now pulls greeting + suggestions from the connected desktop via the tunnel API (
/tunnel/api->/api/suggestions/generate), so mobile web users see the same cached AI-generated pills their desktop already produced. Previously mobile only showed the greeting because relay-only short-circuited the suggestions fetch. - Desktop UI no longer calls
/api/suggestions/generateon its own and no longer renders the "Try asking something like:" label. Suggestions are mobile-only now - the desktop empty state is just the centered greeting + input.
Changes
- Sidebar icon label renamed from "Config" to "Settings" (English only; other locales already said "Settings"/"설정"/"設定").
Site
- Added v0.2.13 blog post with pixel-office hero image; registered in
blog.tsx, worker SPA meta, and sitemap. Post is marked featured so it pins to the top of /blog.
Pixel Office — full redesign pass
- New cozy startup aesthetic: warm cream walls with wainscot trim, light marble-tile floor (6x3 staggered slabs with veining), wood-framed windows with sills, wall decorations (framed picture, clock, cork pinboard), and slowly-rotating ceiling fans. Ambient golden light beams with dithered edges and drifting dust motes near windows.
- Harmonized furniture palette: every piece now draws from the same ~18-color palette. Desks have warm oak tops with a single centered accessory (coffee mug, paper stack, or desk plant - deterministic per position). Chairs are backless 2-wide terracotta stools so a pet clearly perches on the cushion. Plants, lamps, shelves, whiteboards, and the water cooler all use coordinated wood + terracotta + sage tones.
- 2.5D object sprites: tall items (floor lamp 1x2, floor plant 1x2, bookshelf 3x1 drawn 2 tiles tall, whiteboard 4x1 drawn 2 tiles tall) protrude upward beyond their grid footprint without colliding with surrounding furniture. No more "floating head" plants or clipped shelves at the wall edge.
- Balanced layout: 4 workstations (left), meeting room with whiteboard + 3x3 rug + table + 2 chairs (center), lounge with rug + 2 cushions + tall lamp + tall plant (right), kitchenette with water cooler + bookshelf + pet feeder/bowl/cushion (far right). Mix of 1x1 and 1x2 items, perimeter plants and lamps anchor each zone.
- Agent-to-pet assignment is randomized: earlier the leftmost free pet and leftmost free seat always got picked, so the same pet always worked. Now random pick means workstations rotate.
- Clickable agent popover when a working pet is selected: compact 320px card with status dot, pet name, current task, live tool indicator with animated dots, last error, top-used tools, iteration/tool/elapsed stats, and Stop + Open Full actions. Much cleaner than the old cramped popover.
- Real-time pet-position persistence: pet positions (plus direction and state) now save to localStorage every 3 seconds and on tab blur / window close. Returning to the Agents page keeps every pet exactly where it was. Writes are batched so there is only one localStorage set per cycle.
- Notifications are deep-linkable: agent-completion notifications now carry the session id in their payload. Clicking "Agent Done" in the bell popover jumps straight into that agent's detail page. Works across the in-app bell, the Electron titlebar bell, and the Windows header bell.
Pixel Office - earlier in v0.2.13
- Claude-Code-inspired rare encounter journal (18 critters across 5 tiers, seasonal/night/agent-completion gates).
- Welcome-back summary modal with sparks/items/sightings earned while away.
- Pets replace human agent sprites; each walks to a chair and "works" with an animated monitor.
- Background pet simulation always runs when the Pixel Office is enabled (no separate opt-in).
- Wall-clock catch-up simulation so pet stats and rare rolls progress accurately after long absences.
- Server-side state backup via
/api/pixel-office/state(single-row blob store, overwrites in place - no history accumulation). - Visitor system retired and unified under the rare-encounter journal (sightings-only).
- Inventory now surfaces only placeable items; starter seed is a single comfy cushion.
- Auto-forage removed; pets stay home.
Fixes
- Feed sidebar black dot that couldn't be dismissed (legacy Activities-era code).
- Goal-started agents now appear as separate rows in the history list (each run mints its own session id).
- Agent popover used to show 2 running agents for 1 goal because the in-memory "default" row and the fresh session row both appeared - now deduped.
- Sidebar label "Agent Team" simplified to "Agents" across all 10 locales.
- i18n interpolation accepts both
{var}and{{var}}placeholder styles. - Monitor flicker on working pet's desk now aligns to the desk's centered monitor and refreshes at a subtle 2 fps instead of strobing.
- Wall-anchored tall items (whiteboard, shelf) previously got clipped or hidden when placed near the wall seam - render order fixed.
- Settings showed a stray "Background Pet Simulation" toggle even though the feature is always on - removed.
- Notifications now support action buttons. Previously the in-app bell dropdown only showed title + body - there was no way to trigger "Install", "Dismiss", etc. - so the new-version notification in particular had no actionable path. The Electron native OS notification also gains action buttons (macOS) and opens the bell popover when clicked (Windows/Linux), bringing the in-app action chips to every platform.
- "Update ready" notification now posts to the in-app notification center with Install Now / Later buttons, not just a silent system toast. Dedupes across multiple download cycles via a fixed notification id.
- Fix tunnel "restart desktop" and update-install restart paths leaking the spawned Node server subprocess. Previously
app.exit(0)/quitAndInstallskipped thebefore-quitlifecycle, so the server kept running on port 13847 and the relaunched app could not bind the port - making restart worse for users who were trying to recover from a stuck state. Restart now explicitly tears down the server subprocess and tunnel client before handing off to the updater / relaunch. - Fix Claude Code OAuth credential detection on Windows. The previous code path tried to read tokens from Windows Credential Manager via the community
Get-StoredCredential/New-StoredCredentialcmdlets, which are NOT installed by default on Windows - the shell-out silently failed for every Windows user and then fell through to the file fallback. Claude Code on Windows actually stores tokens in%USERPROFILE%\.claude\.credentials.json(matches upstream openclaw / pi-ai reference behavior), so we now read/write that file directly with no PowerShell spawn and no module dependency. Token refresh writes back to the same fileclaude loginuses. - Fix Feed Generator interval changes not applying. Changing "Frequency" in Settings → Feed Generator from 5 minutes to 1 hour updated only the heartbeat-runner but left the scheduler's paired
Heartbeatschedule spinning its ownsetIntervalat the old interval forever. Execution is now owned exclusively by the heartbeat-runner; theHeartbeatentry in the Schedules UI is display-only (scheduler skips attaching a timer for the auto-seeded entry). Settings ↔ Schedules UI are kept in sync both directions: interval/enabled changes from Settings mirror to the schedule row, and toggles/interval edits from the Schedules UI mirror back to HEARTBEAT.md + the runner.
Changes
- Feed Generator is now the single Heartbeat schedule. Users can no longer create additional heartbeat-type schedules from the Schedules UI - that type is removed from the create dialog, and
POST /api/schedulesrejectstype: "heartbeat"requests. Use Cron instead for custom periodic schedules.
Features
- New OpenAI (OAuth) provider: use your ChatGPT Plus/Pro subscription directly — no API key needed. Foxl reads
~/.codex/auth.json(created bynpx @openai/codex login), auto-refreshes the OAuth token, and calls OpenAI's Codex Responses API directly through Strands' VercelModel adapter. No local CLI subprocess, no proxy server. Tool use, reasoning/thinking blocks, and streaming all flow through the normal Strands agent loop. Models: GPT-5.4, GPT-5.4 Codex. - Claude Code (OAuth) rewritten on Vercel AI SDK: drops the
claudeCLI subprocess. Foxl now reads the user's Claude Code OAuth tokens (macOS Keychain, Windows Credential Manager, or~/.claude/.credentials.json) plus long-lived API keys fromclaude setup-token, auto-refreshes OAuth tokens, and callsapi.anthropic.com/v1/messagesdirectly through Strands' VercelModel adapter. Foxl's full tool registry, thinking/reasoning blocks, and streaming now work end-to-end on Claude Pro/Max subscriptions — same path as the OpenAI OAuth integration. - Settings > Providers list now groups each OAuth login right under its API-key counterpart (OpenAI (OAuth) under OpenAI, Claude Code (OAuth) under Anthropic, Gemini CLI under Google) with the provider's own icon.
Changes
- Renamed the Claude Code provider surface from "SSO" to "OAuth" everywhere (UI labels, status field
ssoConfigured->oauthConfigured, docs). The underlying auth mechanism is unchanged — just more accurate terminology. - Removed the legacy Codex CLI provider (subprocess-based). Replaced by OpenAI (OAuth), which talks to OpenAI's servers directly using the same
~/.codex/auth.jsoncredentials created bycodex login. - Removed the legacy CLI-subprocess Claude Code provider.
@anthropic-ai/claude-codeis no longer a runtime requirement — Foxl just reuses whichever credentials Claude Code already stored (keychain entry or~/.claude/.credentials.json). - Settings > Providers help text for Claude Code now points users to
claude /login/claude setup-tokeninstead of asking them to install the CLI. - Subscription-OAuth providers (OpenAI, Claude Code, Gemini CLI) now show a one-line disclaimer in Settings clarifying that they are unofficial community integrations using local subscription credentials — use at your own risk and never share the auth file.
Fixes
- Fix scheduler (cron + heartbeat) + several helper endpoints that would throw
openai-oauth requires async model creation/claude-code requires async model creationwhen those providers were the active default. All internalcreateModel()call sites now usecreateModelAsync()so OAuth-backed providers work everywhere, not just chat.
Features
- New Feed page (replaces Activities): For You tab for AI-curated items, Activities tab for agent event stream, Logs tab with live SSE log viewer. Persists selected tab per user.
- New Feed Generator (was "Heartbeat" in settings): a background agent that periodically surfaces items in the For You feed. Most runs stay quiet; only items marked urgent trigger system notifications, so it can run hourly without spamming you.
- New
feed_addtool exclusive to the Feed Generator executor (not exposed to chat or subagents). Accepts title, body, priority (quiet/urgent), category. - Feed items support Archive / Unarchive / Clear with a clean Inbox/Archive view toggle. Hover anywhere on the row to archive in one click.
- HEARTBEAT.md is the single source of truth for the Feed Generator prompt. Edit it in Settings or in the workspace; both refresh the runner immediately.
- Windows taskbar overlay icon now renders the unread count (16x16 SVG) for parity with the macOS Dock badge.
Improvements
- Sidebar restructured: Chat / Overview / Workspace in the top group; new "Extend" group with Agents, Tools, Skills, Schedules; Connect (Relay, Integrations); Feed.
- Page header uses i18n via
nav.<id>keys (was a static English NAV_TITLES lookup). - Schedules page Recent Runs gets a full filter bar: search, Status, Type (All / Excluding heartbeat / Cron / Heartbeat only / Webhook), Schedule, Time range, Sort. Grouped by Today / Yesterday / This week with relative timestamps.
- Skills page: Enable/Disable-all toolbar button removed, replaced by a subtle counter pill ("3/12 on") with Enable all / Disable all / Invert actions. Per-skill right-click context menu with Enable, Open in Finder, Delete.
- Notification settings redesigned: Behavior (Only in background / Sound) merged into the master toggle card at top; new Feed section with separate urgent/quiet toggles; test scenarios collapsed into a single dropdown plus Send Test button with a live preview card that shows the notification action chips.
- Heartbeat-type schedules no longer emit the "Schedule: ..." system notification or bump the sidebar badge. They only surface via explicit
feed_addcalls. - Feed Generator default interval changed from 5 minutes to 1 hour (recommended). Interval options are now 15m / 30m / 1h / 3h / 6h, with 5m kept for debugging only.
- Settings → Feed Generator card compacted: inline status strip, Frequency and Quiet Hours side-by-side, Instructions collapsed by default. Save button disabled unless there are unsaved changes; a Revert action discards local edits.
Removed
- Live Logs section removed from Config page. Use the Feed page's Logs tab instead (same SSE stream).
Fixes
- Fix black screen on app relaunch: retry loadURL up to 10 times on connection refused instead of showing error page immediately.
- Error recovery page now probes multiple ports (13847-13851) to find server after EADDRINUSE fallback.
- Fix relay model tier check rejecting prefixed model IDs (e.g. global.anthropic.claude-sonnet-4-6) for free tier users.
- Fix crash in Feed Generator settings when
activeHoursis null in the heartbeat runner state.
Features
- Merge Channels and Integrations into a single "Integrations" tab (sidebar Connect section)
- Slack OAuth workspace selector: enter workspace name (e.g. "mycompany") to connect a specific workspace directly
- Slack App registered (OAuth V2, user-token scopes, any workspace installable)
- Relay Slack exchange endpoint deployed with client_id/client_secret secrets
Fixes
- Fix OAuth loopback server blocking on repeated Connect clicks (async close + explicit cancel of previous flow)
- Fix Connect button staying disabled after first click (busy state now clears immediately)
- Auto-register/unregister integration tools (slack, outlook) on connect/disconnect without server restart
Features
- New Integrations page (sidebar → Connect → Integrations) for connecting Microsoft 365 and Slack via one-click OAuth
- New
outlooktool (21 actions): email (inbox/read/send/reply/forward/search/folders/drafts/attachments/contacts/move/categories/update), calendar (view/meeting/availability/room booking/search/shared list), and Microsoft To-Do (lists/tasks/checklist) via Microsoft Graph API - New
slacktool (16 actions) using the user's OAuth token: search_messages, whoami, get_recent_messages, channels_list, conversations_history/replies/add_message/open/members, check_replies_batch, users_lookup/profile_get, attachment_get_data, reactions_add/remove, file_upload - Both tools are skill-gated: only registered after the user connects the corresponding account (no noisy tool schema otherwise)
- Microsoft OAuth uses PKCE + loopback (public client, no shared secret). Slack OAuth exchanges the auth code through relay.foxl.ai so the Slack client_secret is never bundled in the desktop app.
- Deep-link fallback (
foxl://integrations/callback) for environments where the loopback port is blocked - Electron
openExternalIPC exposed for renderer → browser navigation (http/https only)
Changes
- Existing
outlook-webskill (browser-extension DOM scraping) marked as legacy/disabled - superseded by the nativeoutlooktool - Existing
slackskill rewritten from bot-token stub to real user-token tool
Infrastructure
- New
foxl/server/integrations/module: shared OAuth loopback, token store (agent_memory-backed), Microsoft Graph wrapper (auto-refresh on 401), Slack Web API wrapper - New relay endpoint
POST /integrations/slack/exchangeholds the Slack client_secret - Skill eligibility now honors
requires: [integration:<provider>]- auto-hides tools until connected
Improvements
- Migrate to SDK-native cooperative cancellation (agent.cancel()) for faster, more reliable stop
- Stop button now cancels at SDK checkpoint (between model events and before tool execution) instead of polling flags
- Subagent stop also uses SDK cancel
- Strands SDK updated to 1.0.0-rc.3, Bedrock SDK to 3.1030
Fixes
- Fix Opus 4.7 thinking not displaying: Opus 4.7 defaults thinking.display to "omitted" (empty text, signature only). Now explicitly sets display:"summarized" to restore thinking content in responses.
Fixes
- Fix context usage percentage showing inflated values for Opus 4.7 (was using 200K instead of 1M as context window)
- Context window size now reads from shared model catalog instead of hardcoded string matching
- Future models automatically get correct context % without code changes
- Fix Opus 4.7 thinking error: "reasoning content format incorrect" - Bedrock SDK updated to 3.1030 for new thinking block format (thinking field + signature)
Infrastructure
- Add @shared Vite alias for frontend access to shared model catalog
- Strands SDK updated to 1.0.0-rc.3
New Model
- Claude Opus 4.7 support - Anthropic's most capable model, available on AWS Bedrock (us.anthropic.claude-opus-4-7)
- Opus 4.7 set as default model and routing target for expert-level tasks
- Opus 4.6 retained in catalog for backward compatibility
Fixes
- Fix Opus 4.7 thinking error: "thinking.type.enabled is not supported" - now correctly uses adaptive thinking for all Opus/Sonnet 4.6+ models
- Fix Electron EPIPE crash when tunnel log writes to closed pipe (uncaughtException handler)
Fixes
- Fix context compaction triggering at 180K tokens instead of model's actual limit (Opus/Sonnet now compact at 900K, was 180K)
- Fix scheduler cron times interpreted in system timezone instead of user's configured timezone
- Fix hardcoded Asia/Seoul timezone in schedule display (now reads from USER.md)
- Fix heartbeat active hours check using wrong timezone
Improvements
- Context compaction now stays in the same conversation (no more "Continued:" new chats)
- Compaction inserts a visible divider message so users know it happened
- Context usage percentage resets after compaction
- Sidebar conversation list now supports infinite scroll (was limited to 50)
Fixes
- Fix model ID resolution in agent core: canonical IDs now correctly converted to provider-specific ARNs via catalog (was passing
claude-opus-4-6directly to Bedrock instead ofglobal.anthropic.claude-opus-4-6-v1) - Fix subagent model routing: subagents now resolve models through the same catalog as parent agent
- Fix GLM-5 and Kimi K2.5 not receiving
reasoning_configon Bedrock (string-match detection replaced with catalog family lookup) - Fix navbar hash links navigating to wrong page from blog (
#security->/#security)
SEO
- docs.foxl.ai: favicon (SVG), robots.txt, sitemap.xml (21 pages), OG/Twitter meta, canonical URLs per page
- foxl.ai: BreadcrumbList JSON-LD replaces SiteNavigationElement (fixes Google breadcrumb display)
- app.foxl.ai: SVG-only favicon (remove PNG fallback)
Blog
- New post: Foxl v0.2.2 release notes
- New post: What Breaks When Your AI Agent Runs on a Real Desktop
- Blog posts split into individual files with lazy loading (main bundle -28KB)
Build
- foxl.ai: Vite plugin auto-generates sitemap.xml on build
- docs.foxl.ai:
npm run buildauto-generates llms.txt + sitemap.xml from MDX sources
Model Registry
- Unified model catalog: single source of truth for all models across desktop, relay, and providers
- Model IDs normalized to canonical short form (e.g.
claude-opus-4-6instead ofglobal.anthropic.claude-opus-4-6-v1) - Adding a new model now requires editing 1 file instead of 5+
- Usage tracking shows clean canonical names (no more duplicate model entries)
- Relay derives model list from shared catalog
Settings
- Moved Logs from sidebar to Settings > Live Logs (collapsible card)
- System Report: download diagnostic file with system info, conversations, audit trail, and noise-filtered server logs
- Reports generated server-side for speed and richer data (audit.jsonl, DB conversations)
Chat
- Edit messages with attachment management: remove images/documents via X button before regenerating
- Save & Regenerate works even without text changes (useful for re-running with removed attachments)
- Auto-focus chat input on conversation switch (click sidebar conversation or new chat)
Sidebar
- Click "Chat" nav to start a new conversation (when already on chat page)
- Right-click "Chat" context menu: New Chat, Search, Delete All (all i18n)
- Right-click conversation context menu items now fully localized
Relay
- Non-Claude Bedrock models (GLM-5, Kimi K2.5) routed via Converse API instead of Anthropic InvokeModel
- Full tool support and streaming SSE translation for non-Claude models
Browser
- Dedicated Foxl browser profile (separate from personal Chrome)
- Login state persists across sessions within the dedicated profile
SEO
- docs.foxl.ai: add favicon (SVG), robots.txt, sitemap.xml (21 pages), OG/Twitter meta, canonical URLs
- docs.foxl.ai: per-page OG title/description/URL for all docs pages
- foxl.ai: replace SiteNavigationElement with BreadcrumbList JSON-LD (fixes Google showing all nav links as breadcrumb)
- foxl.ai: per-route BreadcrumbList via Worker (blog posts get Foxl > Blog > Title)
- app.foxl.ai: remove PNG favicon references, SVG-only favicon
Dev
RELAY_URL=http://localhost:4200for local dev with foxl relay mode
Skills
- Install skills from [skills.sh](https://skills.sh) registry directly in the Skills page
Scheduler
- Fix cron weekday schedules firing on wrong days after laptop sleep/wake
Overview
- Redesigned dashboard: today-focused metrics, cleaner layout, reduced polling
- Token usage by model with provider labels (Bedrock, Ollama, etc.)
- Usage Explorer shows which provider handled each model request
- Local data disclaimer in Usage Explorer
Search
- In-chat search (Cmd+F): find text within current conversation with persistent yellow highlights
- Cross-conversation search (Cmd+Shift+F): search across all conversations from sidebar
- Multi-word search: "hello world" finds messages containing both words
- Match navigation with up/down arrows and match counter (N/M)
- Click sidebar search result to navigate to exact message with highlights
- Korean and multi-byte text search support
UI
- Increase button spacing in prompt input toolbar for mobile
- Protocol-level WebSocket keepalive for more reliable connections
Multi-Agent
- DB-backed auto-continuation queue — replaces two competing in-memory queues that caused duplicate agent spawns (3 agents becoming 6)
- SQLite lock table serializes drain execution — atomic INSERT OR IGNORE mutex, stale lock cleanup
- Remove injectMessage for subagent completion — fixes raw [SUBAGENT COMPLETED] text appearing in chat during auto-continuation
- Sidebar agent list redesign: elapsed timer, stop button (hover), shadcn Badge for collapsed mode
- AgentChatView: group consecutive thinking+tool items into single Message block (matches main chat spacing)
- AgentChatView: fix thinking blocks not displaying — each DB event rendered as separate timeline item
- Per-agent tool event callbacks — each agent's tool calls scoped to its own instance (no cross-agent leaking)
- Subagent thinking capture — model reasoning blocks (reasoningContentDelta/thinking_delta) recorded to DB
- Auto-continuation triggers per-completion — each agent result delivered independently (no 30s batching)
- Parent breaks after spawning subagents — waits for auto-continuation instead of re-prompting
- Fork children keep full tool list for prompt cache sharing — spawn blocked at hook level
- Block subagent spawning during auto-continuation turn (DB lock = spawn guard)
- Remove goal-based dedup guard (Claude Code doesn't do this)
- AgentChatView fully DB-driven timeline — survives WS disconnect, complete/error events rendered
- Remove duplicate goal message bubble + duplicate result rendering
- Completed agent goal display — merge DB goal into in-memory state
- Remove all subagent result/tool truncation (was 1000-5000 chars)
- Remove duplicate active agents section from Agents page (fork tags sanitized)
Tool Approval
- "Always Allow" auto-approves remaining parallel pending tool calls matching new permission rule
- Load DB permission rules at server startup — "auto approve all tools" was silently ignored (ESM require fix)
Channels
- Auto-reconnect channels on server restart (Telegram, Signal)
Scheduler
- Fix cron weekday field parsing — was completely ignored, causing schedules to run on wrong days
- Support ranges (1-5), lists (1,3,5), and mixed weekday expressions
UI
- Message queue redesign: accent background, line-clamp, hover-reveal delete button
- Terminal session count sync: fix stale closure in WS handler, sessions refresh on spawn/exit events
Skills
- Fix YAML folded scalar (
>) and literal (|) block parsing in skill frontmatter - Multiline description fields now correctly joined instead of showing
>literal
Build
- Fix esbuild banner variable collision (
_cr->__foxl_cr) — prevented production app from starting - Fix 6 pre-existing TS errors: GoogleModel import, ProviderType ('foxl'), PermissionSource ('user'), SQLInputValue
Relay Dashboard
- New unified Relay card on gateway page with connection topology tree
- Desktop node shows online/offline status, platform, connection time
- Web client branches display browser, OS, and connection duration
- Remote restart: restart desktop app from any device via tunnel
- Remote update check: trigger auto-update remotely
- Force disconnect individual web clients by fingerprint
- Session history timeline with audit log (deduplicated, last 10 entries)
- Tunnel toggle integrated into dashboard header (desktop mode)
- Account email and relay connection status in card description
- 15s auto-refresh with visibility-aware polling
- Full i18n support across 10 languages (27 new keys)
Tunnel Cost Optimization
- On-demand heartbeat: idle 5min, active 30s (was 24/7 30s)
- HTTP heartbeat bypasses Durable Object entirely (zero DO request cost)
- Slow/fast heartbeat switching on web client connect/disconnect
- Dead connection detection maintained via HTTP liveness check
- Estimated tunnel capacity: ~630 DAU on $5/mo Cloudflare plan (was ~115)
Mobile Terminal
- Compact quick keys: smaller buttons prevent horizontal overflow
- Scroll-to-bottom button in quick keys bar
- Auto scroll-to-bottom after terminal resize (prevents mobile scroll jump)
Foxl Notes Landing
- White/light theme redesign for foxl.ai/note
- Pricing section removed
- Modernized mockup styling
Landing Page
- "Products" renamed to "Projects" (portfolio tone)
- FAQ rewritten: free/open project, Claude Code/Dispatch comparison, mobile access, privacy
- Hero subtitle: "A personal AI agent... Free and open."
- CTA: "Try it out" with "No account required"
- Footer: "An open project by foxl-ai" (was copyright notice)
- Refund link removed from footer
- Billing/subscription language hidden from Terms and Privacy (comment-preserved)
- Nacho entity reference hidden from legal pages
Fixes
- Model selector: foxl relay models now appear first when tunnel connected
- Discord bot: pricing-disabled instruction prevents outdated billing answers
- Terminal scroll position preserved across resize events
Tunnel Stability
- Fix DO hibernate state loss: persist userId/deviceId to durable storage, restore on wake
- Fix tunnel status: check is_online flag for instant disconnect detection (was only using stale last_seen_at)
Tunnel Cost Optimization
- Durable Object Hibernatable WebSocket API: DO sleeps when idle, wakes on message
- Client-driven heartbeat: desktop sends ping every 30s (no more server-side setInterval)
- Chat delta batching: 100ms window reduces DO wake-ups by ~90%
- Estimated DO cost reduction: $8.38/mo to ~$0.41/mo per 2 tunnel users
Subagent Fork
- Fork subagent: children inherit parent's full conversation as byte-identical prefix for prompt cache sharing
- Identical system prompt + tool definitions across fork children (Claude Code pattern)
- Anti-spawn via fork directive prompt (soft guard) + isInForkChild() code check (hard guard)
Terminal Reopen Fix
- Terminal panel loads scrollback on reopen (no more blank black screen)
- Works for both running and restored sessions
Mobile Chat Copy
- "Copy Entire Text" works on app.foxl.ai (reads from localStorage in relay-only mode)
- Message copy/edit/regenerate buttons always visible on mobile (no hover needed)
Fixes
- Fixed auto-update "Relaunch" button only hiding window instead of restarting on macOS
- Fixed 10+ TypeScript type errors across App, AgentEvent, ImageAttachment, ConversationItem, ScheduleExecution
Native 1M Context Window
- Opus 4.6 and Sonnet 4.6 now have native 1M context window (no beta header needed)
- Removed separate "(1M)" model variants -- all Opus/Sonnet 4.6 are 1M by default
- Simplified model selector: single entry per model instead of two
Adaptive Thinking Effort
- New effort level selector: Low, Medium, High, Max (replaces token budget)
- Adaptive thinking with effort parameter sent via Bedrock
output_config - Max effort is Opus 4.6 exclusive (other models fall back to High)
- Default effort level: Medium
Tool Approval
- Claude Code-style permission prompts inline with tool calls: Allow, Always allow, Deny
- Pattern-based trust: "Always allow" on
exec lscreatesexec(ls*)rule - Permission rules persist to DB across restarts, with
*wildcard as default - Settings page: toggle auto-approve-all, add/remove patterns manually
- Built-in safety: destructive commands (
rm -rf /,mkfs,dd) are always denied
Agent Core v2
- Context compaction: auto-summarize long conversations to stay within context window
- Error retry with exponential backoff for transient failures
- Hook system: BeforeToolCall / AfterToolCall lifecycle for permission checks and plugins
- Fork subagent: spawn child agents from parent conversation context
- System prompt token optimization (34K to 19K chars, 44% reduction)
GLM 5 + Kimi K2.5
- GLM 5 (Z.AI) available on Bedrock + relay with reasoning_config thinking support
- Kimi K2.5 (Moonshot AI) with 256K context, vision, and tool use
Context Usage in HUD
- Context window usage percentage displayed in macOS titlebar HUD
- Per-conversation tracking from actual token metrics
- Real-time updates during streaming, persists across conversation switches
Claude Code SSO Fixes
- Fixed double text output and shimmer stuck during streaming
- Rewrote CLI stream parser for reliable tool result handling
File Attachments
- Drag & drop files onto chat area with visual drop zone overlay
- Accept any file type via paste or file picker (json, yaml, py, ts, etc.)
- Unsupported Bedrock formats (json, xml, yaml) mapped to txt server-side
- Document attachment pill constrained to message bubble (no overflow)
Subagents
- Removed execution timeout — agents run until completion (no more premature stops)
- Auto-continuation max-wait safety timer (30s) for result synthesis
Settings Persistence
- AWS profile, region, and thinking effort persist across server restarts
- Saved to server-config.json in data directory
Schedule Reliability
- Schedules auto-disable after 3 consecutive failures (including stuck task timeouts)
- Prevents infinite retry loops for broken schedules
Large Text Paste
- Virtualized text viewer for attached text blocks (handles 1M+ chars without freezing)
- indexOf-based parser replaces regex (no backtracking on huge content)
- Copy button still copies full original content
Provider Detection
- Bedrock provider correctly reports unconfigured when no AWS credentials exist
- foxl.ai relay models listed above BYOK/bedrock models in model selector
Update Banner
- "Updated to vX.X.X" with Relaunch button in sidebar when auto-update completes
Landing Page
- Animated SoftAurora background with mouse interaction and fade-in
- Redesigned Security section with CSS Grid layout
- Discord button, pricing display, license URL fixes
Electron
- Electron 41, electron-builder v26
- Bundled Node.js runtime for non-developer users
- Source map protection (never ship .map files)
Fixes
- Model/provider sent in chat stream request body (fixes race condition)
- Fixed tunnel double-connection on sleep/wake reconnect
- Fixed production terminal, schedule persistence, provider switching
- Auto-close terminal panel when last session killed
- Fixed spinner stuck after stream completion (agentRunning:false broadcast)
- OS-aware download button in navbar
- Sidebar refresh after OAuth login
Claude Code SSO
- Use your Claude Pro/Max subscription directly in Foxl -no API key needed
- Auto-detects credentials from macOS Keychain or ~/.claude/.credentials.json
- Just run "claude auth login" once, then select Claude Code (SSO) in Settings
- Token-level streaming with thinking/reasoning separation
- Available models: Opus 4.6, Sonnet 4.6, Haiku 4.5 via your subscription
BYOK Relay Passthrough
- Use your own Anthropic API key through app.foxl.ai -zero Foxl credits consumed
- Relay acts as stateless proxy -your key is never stored server-side
- Usage tracked for analytics only (cost: $0)
Terminal Relay Interface
- Built-in terminal in Chat UI -spawn PTY sessions, tabs, split view, resize
- JetBrains Mono Nerd Font bundled (Ghostty-style zero config)
- Remote monitoring via relay tunnel -view desktop terminals from mobile
- Terminal tool: agent can spawn, write, read, kill sessions programmatically
- ANSI strip filter for clean LLM output
Chrome Extension Multi-Tab
- Multi-tab browser control -switch between tracked tabs with labels
- New commands: switch_tab, list_contexts for tab management
- Sidepanel tab bar UI with chips and 3s polling
Bedrock Prompt Caching
- Automatic prompt caching via cacheConfig strategy for Opus/Sonnet/Haiku
- Applied across server, agent, and provider layers
Relay
- Ultra tier upgraded to 10,000 credits/month
- Free tier Opus access blocked (Haiku + Sonnet only)
- Tunnel pending request cap (50) + chat stream cap (10) for stability
Async Subagents
- Auto-continuation: when all subagents complete, main agent auto-synthesizes results
- Completion queue prevents concurrent auto-continuations on same conversation
- Schedule persistence across app restarts
Agents UX
- Terminal fullscreen mode in chat panel
- Crew-style suggestion cards on empty chat screen
- AI-generated suggestions with structured defaults
Skills Sync
- Auto-sync skills from foxl-ai/skills repo on first install
- Skills sync button in SkillsPage UI
- Local edits preserved via content comparison during sync
Mobile
- Terminal persistence, mobile IME fix, mobile terminal UI
- Mobile terminal return icon and Del key
Windows Support
- Native Windows desktop app -NSIS installer + portable exe
- CRLF line ending normalization for skills parsing
- PowerShell terminal with smart shell detection (pwsh.exe, powershell.exe)
- Native Windows title bar, portable Node.js detection
- Code signing with self-signed certificate
Auto-Update Fix
- Universal build targets -single DMG/ZIP works on both Intel and Apple Silicon
- Fixed latest-mac.yml referencing non-existent arch-specific files
Discord Integration
- Discord Q&A bot with /ask slash command (Cloudflare Worker)
- Gateway bot with presence detection and auto-respond in channels
- Relay auth with KV token persistence for bot sessions
Release Automation
- Changelog automation pipeline: CHANGELOG.md as single source of truth
- Release scripts:
release.sh(full desktop release),sync-changelog.sh(changelog-only deploy) - Generated changelog surfaces: foxl.ai/#changelog, foxl.ai/changelog.md, GitHub Release notes
Fixes
- Enable AI suggestions in relay-only mode via tunnel
- Terminal mobile return button visibility and light theme text color
- app.foxl.ai: relay-only sidebar now correctly shows only Chat and Account pages
- Message copy handler preserves hyperlinks as "text (url)"
- Gateway page i18n -hardcoded strings replaced with translations
- Terminal line ending normalization for cross-platform PTY compatibility
- Terminal tool: backspace character processing for clean AI output
- Terminal tool: literal \n escape handling for reliable command execution
- Schedule execution prevConvId not defined error
- esbuild-bundle server to fix 5+ minute NSIS install time (16K to 420 files)
- NSIS installer auto-closes running Foxl before install
- Windows taskbar icon uses exe embedded icon for proper size
- 800+ translation keys across 10 locales (en, ko, ja, zh, es, fr, de, pt, ru, ar)
Auto-Update
- VSCode-style silent auto-update -download in background, notify when ready
- Settings toggle to enable/disable auto-update
Tunnel: Login = Access
- Pairing code flow removed -relay auto-registers device on connect
- Web clients connect immediately (no approval blocking)
- E2E encryption (ECDH) on all WebSocket messages
Tunnel E2E Streaming
- Chat streaming via relay tunnel with SSE passthrough
- Image and document upload through tunnel
- Reconnect recovery with snapshot hydration
Fixes
- Electron UA: real platform/arch (was hardcoded Intel Mac)
- Session detection: Foxl/ UA parsing for device info
- Model naming: "Opus 4.6[1m]" style (Claude Code convention)
- sharp added to server-bundle for screenshot tool
Multi-Agent Thread UI
- Codex-style subagent orchestration with collapsible thread sidebar
- Real-time subagent streaming via WebSocket events (200ms batched flush)
- Event-driven child-to-parent result injection on subagent completion
- SpawnIndicator inline in parent chat with live elapsed timer
- Thread history endpoint (GET /api/agents/:agentId/history)
Streaming Recovery
- Snapshot endpoint for instant page-refresh recovery (text, tool calls, threads)
- Sequence-based SSE subscribe with gap detection
- Recovery state machine (idle, fetching_snapshot, subscribing, live, disconnected)
- WS streaming_state enrichment with currentText, toolCalls, threads
- Fallback to SSE replay when snapshot unavailable
Model Selector
- app.foxl.ai now follows desktop model/provider selection via tunnel
- Desktop BYOK models visible in relay-only mode when tunnel connected
- Model selection syncs back to desktop via tunnel API
- Fix: model selector silently overriding local model with foxl relay
SDK
- Strands Agents SDK upgraded to v0.7.0 (Plugin system, prompt caching)
Fixes
- Credit exhausted popup removed from AccountPage
- Chrome extension Pilot renamed to Foxl branding
- Default provider fallback changed from 'foxl' to 'bedrock'
- WS multi-subscriber Set pattern (prevents callback overwrite)
Tunnel
- Tunnel resilience: heartbeat 3x failure reconnect with backoff reset
- Online-gated routing: __foxlTunnelOnline flag, sidebar isOnline
- Tunnel auto-refresh on online/offline transition
- Load desktop conversations via tunnel API when connected
Chat
- Mobile-first ChatPage layout (relay-only always full-width)
- Model ID resolution: compound prefix + version suffix handling
- WS 4003 token mismatch auto-reload
Fixes
- Chrome extension health check 500ms AbortSignal timeout
- GatewayPage device field mapping normalized
- Provider selection persists via localStorage key
- All model tiers accessible on all plans (restriction removed)
Web App
- app.foxl.ai: relay-only chat with localStorage conversations
- OAuth hash login, Google/Apple sign-in on web and desktop
- AccountPage: credits display, subscription, lazy sessions
- Onboarding flow: welcome, extension, login, chat
Relay
- Welcome 200 credits on signup, monthly 10 credit refill (cron)
- Atomic credit deduction (D1 batch transactions)
- D1-backed rate limiting, OAuth CSRF protection
- Strict model ID validation, 5 Claude models enabled
- relay-worker/ consolidated to relay/
Desktop
- Universal macOS build (arm64 + x64)
- Random port + connection token (VSCode-style security)
- Server crash protection, CORS hardening
- sessions_status tool: waitSeconds parameter
Docs & Infra
- Starlight documentation site (16 pages, full-text search)
- llms.txt / llms-full.txt from real documentation
- CI workflows: deploy-app, deploy-docs, deploy-relay, deploy-site
- Version unified to 0.1.2 across all packages
Agent
- AgentsPage: make expanded session card scrollable
Chat
- Channel: group mention filter, Signal greeting, conversation history API
UI
- Topup UI, webhook idempotency, tray menu, deep link, build fix
- i18n: add useTranslation hook to all 29 sub-components
- i18n: apply t() to 109 strings across 6 pages
i18n
- i18n expansion (496 keys, 10 locales), dev data path fix
- 251 keys across 10 locales, sync script, comprehensive coverage
- Expand to 10 languages, system language detection, complete translations
Skills
- Revert nested skills scan, use symlink instead
- Support nested skills dir + add .omc to gitignore
Fixes
- Fix SettingsPage crash: add useTranslation hook to main component
- Fix i18n: add useTranslation import and hook to all pages
- Fix DialogTitle accessibility warning, add PILOT_SKIP_SERVER
- Fix message queue leaking across conversations
- CI cache-dependency-path for setup-node
Other
- Add Foxl proprietary license
- Security: path traversal protection, JWT production guard
- Show git commit hash in app version (Settings > About)
Agent System
- OpenClaw-parity heartbeat runner with active hours, stuck detection, requests-in-flight skip
- Context overflow auto-continuation (creates new conversation with summary)
- Subagent spawn with result/error saved to conversation messages
- Subagents blocked from spawning other subagents (infinite recursion prevention)
- WebSocket reconnection restores agent state automatically
Chat
- Tool call display with per-tool icons (15 icon types: Monitor, Globe, Terminal, Database, Git, etc.)
- Human-readable tool titles for all 20+ tools
- Confirmation component (shadcn AI pattern) for tool approval
- Extended thinking with collapsible reasoning display
- Image upload, paste, and document attachment support
Skills
- 68 built-in skills (ported from OpenClaw)
- Skills always loaded regardless of binary availability (install at runtime)
- Lazy skill loading (list only, read on demand) for token savings
- Skill eligibility check with missing dependency display
UI
- Agents page redesign: date grouping, time range filter, status filter
- Agent detail dialog: stepper activity timeline, tools used summary, last response
- Settings: Heartbeat config card, provider SVG icons, active hours selector
- Workspace/Skills: Cmd+F search with content search, search button
- Usage Explorer: 30-day default, estimated cost per model
- Dock badge: notifications + unread chat count
Providers
- 7 relay proxy providers: Bedrock, Anthropic, OpenAI, Google, Groq, xAI, DeepSeek
- 24 local providers via OpenAI-compatible API
- Provider SVG icons (light/dark mode)
- Credit system with 10% margin (1 credit = $0.04)
Channels
- Telegram bot integration (non-blocking start)
- Signal CLI integration (foxl-ai/signal-cli)
- Channel send tool for proactive outbound messaging
- Initial Electron desktop app with embedded server
- Pixel Office view with agent characters and pet system
- Code splitting: 14 lazy-loaded pages (1,407KB to 177KB main bundle)
- Token usage tracking with SQLite persistence
- Session memory with LLM-generated summaries
- Context compaction via Bedrock compact API
- Tool approval system with always-allow option
- Browser automation with accessibility tree snapshots (93% token reduction)
- Workspace memory system (SOUL.md, USER.md, MEMORY.md, daily logs)
- Scheduling system (cron, heartbeat, webhooks)
- Multi-provider support (Bedrock, Anthropic, OpenAI, Google, Ollama)