Skip to content

Locus v0.1.5

Choose a tag to compare

@github-actions github-actions released this 03 Nov 05:46
6637c8c

Install: download locus-v0.1.5-arm64-v8a.apk below and open it on an Android 8.0+ phone (allow installs from your browser or file manager when asked). It's the one for nearly every phone; locus-v0.1.5-x86_64.apk is for x86_64 Chromebooks and emulators. Both are signed with Locus's release key; check them against SHA256SUMS.txt if you like.

Locus is in active development, and the way data is stored can still change between versions. See CHANGELOG.md for what's in it.

A big release: voice mode, a real terminal, Linux you can manage from the Workspace, Telegram remote control, interactive artifacts, remembered approvals, new ways into Locus from the rest of Android, and an opt-in update check. It also closes a long list of security gaps found in review.

Tested how? Everything here is unit-tested and passed CI. Most of it hasn't been tried on a real phone yet. Entries say Not yet tried on the phone where the design notes record that, and docs/STATE.md keeps the full list.

Highlights

  • Voice mode: a hands-free conversation with the agent, with the same tools and approvals as typing.
  • An interactive terminal in Locus's Linux, with tabs and an extra-keys row.
  • Linux in the Workspace: install, repair, remove and check Linux from Home → Files, with a health page and a package list.
  • Telegram remote: approve background tasks from Telegram, or start tasks by messaging your bot for a time you choose.
  • Interactive artifacts: charts, diagrams and small tools in a reply run as live, sandboxed previews.
  • Remembered approvals: "Don't ask again" for one tool and one site, folder, recipient or command, in this chat or for 30 days.
  • Ways into Locus: launcher shortcuts, Direct Share, a quick settings tile, a home-screen widget, "Ask Locus" on selected text, the default assistant role, and automation intents for Tasker and scripts.
  • Opt-in update check: off by default; when on, Locus finds a new release, verifies the APK and hands it to Android's installer.
  • Privacy: after a task reads your private data, opening a new site with anything in the address asks first, so a PIN can't leave in a short link.

Security

  • Private data can't leave in a short link without asking. See docs/design/taint.md, "Disclosure".
    • Before, opening an address was an observation, allowed in read-only and Workspace chats, and it only asked when the query was over 300 characters. So after reading a file, the agent could open https://site.example/4821 and send a PIN or a line of the file with no card.
    • Once a task has read your private data (files, memory, chat history, mail, calendar, a contact, git diffs, deploy settings), opening a new host with anything in its path, query or fragment asks, whatever its length, in every chat mode. A background run refuses it instead.
    • Searching (DuckDuckGo, Google, Bing), Wikipedia articles, sites you named or set up, local servers, bare hosts and sites the task already opened work as before.
    • Allow site for the rest of a task. The card offers Allow site ("Allow images.unsplash.com for the rest of this task"), so a mood board of ten photos needs one tap instead of ten. It covers exactly that host, only until the task ends, and is never saved: the next task asks again, another host still asks, and background runs still refuse. It isn't offered when anything besides the address made the call ask. Voice mode and Telegram can't answer these cards.
  • Remembered approvals are strict. See docs/design/remembered-approvals.md.
    • A command rule such as "Always allow git grep TODO" used to cover any command that started with those words. Options that make a harmless tool run another program got through (git grep TODO --open-files-in-pager="sh -c '…'", tar --checkpoint-action=exec=…, zip -TT …, git -c core.pager=…). Now words after a rule's own words must be plain paths or words, with no quotes, = values, globs or shell syntax. Program-running options are refused in every spelling (--opt=v, --opt v, abbreviations, bundles such as -nO) for git, tar, zip, rg, find, Gradle, npm and go.
    • sed, awk, pagers, editors, and wrappers that run another program (env, xargs, nice, timeout, nohup, sudo, su, flock, strace) are never remembered.
    • Once a task has read outside content (a web page, mail, Telegram, an MCP result), a command rule no longer runs a command without asking. The card says which rule was held back and why.
    • A folder rule can't be redirected through a symbolic link. With src/alias -> private/, a write to src/alias/config.txt used to land in private/ without a card. Now a path that goes through a symbolic link, or resolves anywhere but where it says, is neither covered by a folder rule nor offered as one, and the check runs again right before the write.
  • A skill or helper name can't leave its folder. skill_read_file used the skill name as its folder without checking it, so a name such as ../.. let the agent read Locus's other private files (settings, chats, memory) without asking. Every entry point that takes a skill or helper name now accepts only a valid name, and the folder must be a direct child of the skills or helpers folder and not a symbolic link. Linked skill or helper folders are no longer listed or loaded.
  • Skills someone else wrote are outside content. What skill_use and skill_read_file return from a skill imported from GitHub or a zip, or drafted by the agent, now taints the task like a web page: sending, saving memory or opening a host you didn't name then asks. Skills you wrote yourself are unchanged.
  • Remote execution stays inside the project folder. See docs/design/remote-execution.md.
    • Sync-back could be made to write, back up or delete files outside the project by a name such as .. or a folder that was a link. Names that can't be a plain file are now refused, and every path is checked right before each operation. Refused files are named in the result.
    • Uploads don't follow links, and a project folder that is itself a link outside the workspace isn't synced.
    • Each sync downloads into its own temporary folder, so two projects syncing at once can't delete each other's downloads.
    • Replacing a file on the machine moves the old one aside first and restores it if a step fails.
    • The vault-value check streams each file in constant memory and now covers every file, not only those under 2 MB.
  • Another app can't pass itself off as an app you always allow. When Android didn't name the app starting a task, Locus took the creator of the result PendingIntent as the caller, but Android says who made a PendingIntent, not who handed it over. Now only getCallingPackage() and, on Android 14+, getLaunchedFromPackage() name the caller. A request with no named caller always shows the sheet and can't be allowed always, so apps that use plain startActivity (such as Tasker's Send Intent) are asked every time. See docs/design/android-bridge.md, "Who may ask".
  • A workflow input can't switch off the workflow's approval points. An input containing </workflow_plan>, which another app could send through the automation intents, cut the plan short and dropped its always_ask steps, allowed_tools fence and checks. Inputs containing Locus's tags are now refused, < is escaped inside the plan, the plan is read to the last closing tag, and a plan that doesn't read back fences the run (every call that changes something asks). Existing chats keep their cached prompt. See docs/design/workflows.md.
  • Provider addresses, proxies and SSH host keys are sealed. A program that changed the settings file could point a provider at its own server, add a proxy or CA certificate, or swap an SSH host key, and the next request would send the API key there. These are now sealed with their own Keystore key (EndpointSeal). If the seal breaks, each provider shows "Check address" and sends nothing until you save it, proxies and imported certificates are removed, and SSH host keys are cleared. Existing installs are sealed on update without an alert. See docs/design/linux-isolation.md.
  • A connector can't borrow another connector's key or sign-in. An injected page could have the agent draft a connector at its own address naming GitHub's key. Now an OAuth connector uses only its own sign-in, and a token goes only to the server it was issued for (RFC 8707). A key goes only to the address it was saved or confirmed for. The agent can't draft a connector that names another connector's key, a built-in's key or a key already in the vault, and a draft's card says whose key it would use and where it goes. Existing connectors keep working; a key shared between addresses shows Confirm key once. See docs/design/mcp-connectors.md, "Keys belong to an address".
  • An email approval card shows where the mail really goes. For "alice@company.com" <drop@attacker.example>, the card showed the trusted-looking name first. Cards now list only the parsed addresses, for to and cc, and a display name that looks like an address is refused when sending.
  • A tool call that changes something is never re-run on an MCP server's word. A server could do a write, then fail with text containing "401", and get it run twice. Now only an HTTP 401 from the transport counts as a refused sign-in, only read calls are retried, and for any other call Locus renews the sign-in and tells the agent the call may not have run.
  • A restore can't fill the phone. Extracting a backup stops, as damaged, once files unpack to more than the manifest declares (plus 10%, at least 1 MB) or past 500,000 entries, and it checks there is room before writing anything.
  • The Activity log hides secrets. Vault values, Authorization headers and cookies, tokens and API keys, password and one-time-code fields, passwords in URLs and secret-looking query values show as a label such as [redacted: password]. The stored log and what the model gets are unchanged.
  • Each chat has its own browser. Two tasks browsing at once used to share one set of tabs, so one chat's navigation could change what another chat's next click hit. Each chat and each helper now has its own tabs, and a snapshot's element refs only work on that tab and page. There are at most 8 tabs across chats; idle ones are closed first. Logins are still shared. See docs/design/browser.md, "One browser per chat".
  • Downloads follow the chat that owns the page. Whether a page may download into the workspace was one global switch, so a read-only scheduled run's page could download while another chat was browsing. It's now per chat and checked when the download starts.
  • The browser's safety check can no longer be answered by the page. Each tab has a random secret that only Locus's page helper knows, and every answer must carry it. If the helper can't run first or a page defines its own, clicks, submits and typing ask you. A malformed answer counts as no answer. The agent never types into a field it couldn't check, and the page is re-checked for password, card and one-time-code fields in the same step that types. See docs/design/browser.md, "Trusting the page helper".
  • fetch_url can't be turned to the LAN by DNS rebinding. It checked a host's addresses and then connected by name, so a server answering with a public address first and 127.0.0.1 second got through. Its own client now checks every DNS answer at connect time, redirects included, and doesn't use a system proxy. See docs/design/network.md, "fetch_url and DNS rebinding".
  • Programs in the terminal can't set the clipboard. An OSC 52 escape sequence used to replace the clipboard silently, so what you pasted next could be a command you never copied. Locus ignores those writes and says so once per tab.
  • The Telegram chat id and remote mode's start time are sealed with the permissions, so a program editing the settings file can't point remote messages at another chat. Existing installs migrate without an alert: the Telegram chat link is reset once.
  • A warning for plain http:// to a public MCP server in the Add an MCP server form, as the provider editor already had.
  • Documented: Locus and its Linux share one user. SECURITY.md and docs/ROADMAP.md now say what that means: the protections against Linux programs are not a kernel boundary, and same-user races remain possible.

Features

  • Voice mode. A full-screen, hands-free conversation with the agent in the current chat: it listens, sends what you said, reads the reply aloud, then listens again, with the same tools, approvals and caching as typing. Hold the mic, use + → Voice mode, or the Voice launcher shortcut. See docs/design/voice-mode.md. Not yet tried on the phone.
    • A live level and partial transcript. Tap or talk over Locus to interrupt it. Replies are read without code, tables or tool chatter; long ones stop with "the rest is on the screen". An optional short spoken status plays while tools run.
    • What you say while the agent works goes in as a mid-task note, and an open question from the agent is read aloud for you to answer.
    • Approvals by voice only for plain Ask-level actions ("allow", then "confirm", or "deny"). Anything after outside content, anything the reviewer flagged, and payment-like actions need a tap, and the screen says why.
    • Android's speech services only, on-device recognition preferred. No audio is kept, and the microphone is on only while "Microphone on" shows, never in the background. It pauses for calls and other audio, and ends after 30 s of silence or with End.
    • Settings → Voice: speech speed (also for read-aloud), Android's voice settings, Listen after each reply, Say what it's doing, Interrupt by speaking.
  • An interactive terminal. Workspace → the terminal icon, or "Open terminal" in a chat's terminal panel: a login shell in Locus's Alpine Linux on a pseudo-terminal, with /workspace mounted, so vim, htop, less and coding CLIs work. See docs/design/terminal.md. Not yet tried on the phone.
    • Up to six tabs, which keep running when you leave the screen and stop when you close the app; 5,000 lines of scrollback each.
    • An extra-keys row (Esc, Tab, Ctrl, Alt, arrows, |, ~, /, Home/End, PgUp/PgDn, Paste); long-press to select, copy and paste; pinch to change the text size.
    • "Ask Locus about this" puts the selected text in a new chat's composer, unsent.
    • It's you typing, not the agent: nothing in the terminal goes to the model, and the shell has exactly the agent's isolation.
    • Built from Termux's terminal-emulator and terminal-view (Apache-2.0), vendored as source: no prebuilt binary and no JitPack.
  • Linux in the Workspace. Home → Files opens with a Linux card, so you no longer have to re-run setup to install Linux. See docs/design/linux-setup.md and docs/design/linux-health.md. Not yet tried on the phone.
    • Install from the card with the tools you pick and an estimate of download, space and time. The install keeps going if you leave, shows a notification with Cancel, and cancelling leaves nothing half-installed. A failed install says why and offers Try again.
    • Once installed, the card shows the version, the space used and running services (each can be stopped), and offers the terminal, Add tools, Repair, Reinstall and Remove. Removing keeps your workspace files.
    • Health & details: a verdict (OK, Warning or Problem), the problems with fixes you can run from the screen (Repair, Reinstall, Clean caches, Reset DNS, Restart a service, or how to turn off Android's child process restrictions), phone storage with the biggest folders in Linux, memory when the phone is low, services, and on request DNS and internet checks.
    • Packages: "42 you installed · 214 total", with All as a filter, search, each package's description, Uninstall for your own, and Check for updates.
    • When Android kills a Linux process in the background (the phantom process limit), the card explains it and gives the fix. Onboarding uses the same installer, and the terminal and agent point here when Linux isn't installed.
  • Telegram remote: approve by button, and remote mode. Both are off by default (Settings → Integrations → Telegram → Remote), need a private chat with your bot, and are sealed with the permissions. See docs/design/telegram-remote.md. Not yet tried against the live Telegram API or on the phone.
    • Approve from Telegram. When a background task asks for approval, the request comes to your bot chat with Deny and Allow buttons. A press counts only from your chat and your account, and only once: each request carries its own random code, which dies when it's answered, times out or is answered in Locus. Anything the reviewer flagged or that must be confirmed shows without buttons and says to open Locus.
    • Remote mode. Messages to the bot start a task in a chat called Telegram, or join the running one as a note, and the answer comes back to Telegram. It's on for a time you pick (30 minutes to 8 hours); a notification with Turn off shows meanwhile, home shows "Telegram remote on until 14:30", Settings shows Remote on, and /off to the bot stops it.
    • Messages from Telegram are outside content (the run is tainted), messages sent before you switched it on are ignored, and more than six a minute are dropped. By default Telegram tasks can look and answer but not change, run, send or ask; "Let Telegram tasks do more than read" lifts that, with a warning.
    • Your messages are stored in a small sealed inbox before Telegram is told they arrived. A failure is retried up to three times and you're told in plain words; a task that has started is never run twice.
    • Locus polls Telegram only while an approval with buttons is waiting or remote mode is on. The bot token stays in the vault and never reaches the model or the logs.
  • Interactive artifacts in chat. A reply's ```html artifact or ```svg artifact fence, or an .html or .svg file the agent sends, shows as a live preview card with Full screen, Save to workspace, Share as image and View source. A plain ```html fence stays code. See docs/design/artifacts.md.
    • Sandboxed: JavaScript runs in a frame on its own opaque origin with no network (only data: and blob:), no storage or cookies, no file access, no JavaScript bridge and no navigation. Links ask "Open in browser?" first. Artifacts are capped at 2 MB.
    • Light on memory: one preview runs at a time; the others show a snapshot until you tap them.
  • Remembered approvals. An approval card can offer "Don't ask again for …" with This chat or Always (30 days). Each choice covers one tool and one scope: a site, a folder, one email or SMS recipient, or a command prefix such as ./gradlew test. Payments and forced confirmations, anything the reviewer flagged, targets from outside content and dangerous commands are never remembered. A remembered approval only turns an Ask into an Allow; Deny, read-only and project limits still win. Rules are sealed with the permissions; review and revoke them in Settings → Permissions → Remembered. See docs/design/remembered-approvals.md.
  • Ways into Locus. See docs/design/android-bridge.md. Not yet tried on the phone.
    • Launcher shortcuts (New chat, Voice, Workspace, plus pinned and recent chats), which are also Direct Share targets.
    • An "Ask Locus" quick settings tile, and "Ask Locus" in other apps' text selection menu.
    • A home-screen widget showing what Locus is doing ("Working: running a command", "1 approval waiting", "Idle"), the latest scheduled task's result, and an Ask Locus button. Tapping a waiting approval opens its chat; nothing is approved from the home screen. The task line is scrubbed of secrets and vault values, links and Markdown. It updates only when the status changes.
    • The default digital assistant. Pick Locus in Android's Default apps (Settings → Automation → Assistant); the assist gesture opens the ask bar. Locus doesn't see the screen you were on.
    • Automation intents for Tasker, Automate and scripts: start a chat with a prompt (optionally in a project), run a saved workflow with inputs, or run a scheduled task now, with the result returned through a PendingIntent. Off by default (Settings → Automation). Each request is shown to you first unless you chose Always allow for a named app or granted it the "start Locus tasks" permission. What another app sends is untrusted: the run is tainted from the start and runs in a new chat with your usual approvals.
    • Nothing is sent until you tap Send.
  • Opt-in update check. Setup's privacy step asks once, "Check GitHub for new versions?", off by default; the same switch is in Settings → About & privacy, with Check now, which works with it off. See docs/design/updates.md. Not yet tried on the phone.
    • About once a day on Wi-Fi, one unauthenticated request to GitHub's latest-release API, respecting its ETag and rate limits. Drafts and pre-releases are ignored.
    • A newer version gets one low-importance notification and an "Update available" card with the release notes, Skip this version and the release page.
    • Download and verify fetches the APK for your phone's ABI with progress and Cancel, checks its SHA-256 against SHA256SUMS.txt (or GitHub's digest), checks it's a newer Locus signed with the same key, and hands it to Android's installer for you to confirm.
    • Each failure says what to do: no connection, rate limit, no APK for the phone, a checksum mismatch, a different signing key, "Install unknown apps" off, a cancelled install, not enough space.
    • Debug builds can look but not install; installs from F-Droid, Droid-ify, Neo Store or Obtainium leave updates to that store; -Plocus.updateCheck=false builds leave it out. The README explains Obtainium.
  • Memory. See docs/design/memory-search.md.
    • Ranked search in any language. memory_search uses a full-text index ranked by bm25() and returns the best matches first, with topic, summary and matching text. Accents are folded ("cafe" finds "Café"), and a trigram index finds Chinese, Japanese and Korean words. Nepali and other non-Latin queries work now; before, they returned nothing. Non-Latin topics keep their own file names.
    • Memory read, in the chat. When the agent reads or searches memory, the steps card lists the topics it read, with Forget beside each. Not yet tried on the phone.
    • Where each memory came from. Library → Memory shows who saved each note (you, Locus in a chat, or Locus after a task), when, and the chat it was saved in. A note saved after reading outside content says so ("a web page", "an email").
    • Project memory shows under the project's name in Library → Memory, where you can read, edit and forget it, or Move to general so every chat can use it.
  • Scheduled tasks you can follow. See docs/design/scheduled-runs.md.
    • A scheduled run's notification opens the chat it ran in. Tasks → a task → Open last run goes to its latest run.
    • Run now shows "Queued", then "Running now", and a message when it ends.
    • Edit changes a task's name, prompt, interval and conditions in the same sheet that creates it; a scheduled workflow keeps its plan.
    • Tasks and Library → Workflows offer the same intervals, so workflows can now run weekly. Tasks shows an error with Try again if the list can't be read.
  • Completion check. When the agent ends a turn while something concrete is still open (checklist items pending, a failed test or build not yet passed again, or files edited after the last test), Locus asks it once to finish, verify, or say plainly what isn't verified. A failed test stays open until a later run of it passes, you say to skip it, or the reply says it's unverified. At most once per task; skipped in scheduled, read-only, plan-mode and helper runs. The task receipt says when it fired. Not yet tried on the phone.
  • Estimated spend in Insights. Today, 7 and 30 days, a bar per day, broken down by model and by source (chat replies, helpers, the reviewer, titles). It uses the opt-in models.dev prices, says "Unknown" rather than $0 where there's no price, and on-device models count as free. With prices off, the card offers Turn on prices. Not yet tried on the phone.
  • Phone tools. calendar_read reads upcoming events (asking for calendar access the first time), contacts_pick opens the system contact picker (Locus holds no contacts permission), and photos_pick opens the Photo Picker and copies what you pick into inbox/. All three ask by default, are found on demand, and work only while Locus is on screen. The composer's + sheet also gains Photos and Camera.
  • Browser screenshots of the whole page. browser_save takes an optional full_page: the page scrolled through and stitched, with fixed bars hidden after the first screen, up to 15,000 px and 8 megapixels. Not yet tried on the phone.
  • Where commands run, while creating a project. The project editor shows it for a new project too, and with no SSH machine yet it offers Add an SSH machine.

Fixes

  • One run per chat, and each run reports its own result.
    • Two starts arriving together (a Telegram message and a tap, a helper's report and a queued message, the widget and the intent API) could both start an agent on one chat. Taking a chat is now one atomic step, and a finishing run can no longer free a newer run's chat. Sending a message in a scheduled run's chat queues it, and a message sent just as a turn ended starts the next turn.
    • Telegram and the intent API used to return the chat's last reply, so a task that stopped without one could send back the previous task's answer. Each run now has an id, and the caller gets that run's own reply or how it ended ("Stopped: …"). An intent-API run that didn't finish reports FAILED, and a scheduled task stopped by its budget is recorded as stopped.
  • A scheduled task runs once when Run now and its schedule start together. The run is claimed with one database update before anything happens, and editing or pausing a task while it runs can't wipe out that claim.
  • Provider errors in plain words. A failing gateway no longer puts its HTML page in the chat. It says what happened and what to do: "OpenAI's gateway timed out (HTTP 504). This is usually temporary. Try again." 502, 503, 504, Cloudflare's 520–524, 529, 429, 401, 403 and 404 each have their own line; the full body goes to Diagnostics, scrubbed.
  • "Reviewer unavailable" says why: it timed out (usually a thinking model; a link to choose a fast helper model), its key was refused, it's out of credit or replied in a form Locus couldn't read, or a temporary error. You're still asked in every case.
  • Replies no longer hang for up to 10 minutes after a network drop. A stream silent for 5 minutes (3 once it has sent a keepalive) is retried, and keepalives count as activity, so long thinking isn't cut off. Switching between Wi-Fi and mobile data restarts a streaming reply on the new network at once. See docs/design/stream-timeouts.md. Not yet tried on the phone.
  • A model stream can no longer fill the phone's memory. Lines, events, whole streams, tool arguments and text blocks have limits far above any real reply; past one, the stream fails with a clear error instead of growing until Locus is killed.
  • A failed summary is no longer silent. When summarising earlier messages fails, the chat shows "Couldn't summarise earlier messages" with the reason, and Stop during a summary stops the turn too.
  • Linux processes killed in the background say why: "Killed by Android (phantom process limit)", with the fix for your Android version. Not yet tried on the phone.
  • A reply served by Anthropic's fallback model is priced as that model. The reply records the model that answered and the one it fell back from, and task budgets count a declined attempt that was billed. See docs/design/fallbacks.md.
  • Browser screenshots are no longer partly blank. After the browser panel had been shown and closed, browser_save pictures had page content only where the panel had been, and scrolled pages were shifted. A hidden window is now lent for the capture, Locus waits until the page has drawn, and says so if it can't. Transparent pages get a white background, and a tall image's thumbnail shows its top. Not yet tried on the phone.
  • Cloudflare static deploy works again. cloudflare_deploy_static failed with 10021: No such module: locus-static.js because of a malformed multipart header. The Worker module, asset uploads and Supabase's function upload now send it as wrangler does. Not yet tried against the live Cloudflare API.
  • Library no longer opens blank or crashes on a failed action. It shows a spinner while it reads and an error with Try again if reading fails, and a failed action says why in a banner.
  • Lighter chat screen in long chats. An open chat no longer re-reads itself when another chat or a helper saves a step; it reads only new messages. In a 400-message test chat with a helper working, that went from about 36,000 rows read to 410. Unchanged rows are skipped when redrawing. See docs/design/transcript-reads.md.
  • Cancellation is respected. Vault reads and Telegram calls stop when their task is cancelled instead of carrying on or being swallowed.

Design

  • Settings links everywhere. A hint, notice or error that names a Settings page now opens it, and back returns you. "OpenAI has no API key. Add it in Settings → Models." shows Open Models; the same goes for ChatGPT sign-in, the Library's GitHub token, the chat's sheets, the project editor, Scheduled tasks, Network, Permissions and the start-up notices. The local network question links to Android's App info page. See docs/design/design-system.md (SettingsLink).
  • A design system for every screen. New tokens (spacing, radius, icon sizes, strokes, emphasis levels, motion, an Info tone, and tone containers whose text passes 4.5:1 contrast in both themes) and shared components (SwitchRow, KeyValueRow, LocusChip, Meter, InlineSpinner, ErrorState, ConfirmSheet instead of dialogs, one LocusFeedback channel for "Saved" and "Undo"). See docs/design/design-system.md.
  • It's enforced. DesignGuardTest (in CI) counts raw sizes, colours, alphas, dark-mode checks and Material components with a Locus counterpart per file. A file can't go above its baseline, a new file must be clean, and the baseline only goes down. The rules are in docs/design/design-system.md, CONTRIBUTING.md and the PR template.
  • Screens moved over: the browser (page dialogs open as sheets; "Clear browsing data" shows progress and says when it fails), workspace file previews, Activity (shared filter chips and search, full-size touch targets), Library's tabs, Tasks, the Telegram sheet, Mermaid diagrams and artifact previews. Code and Markdown colours follow the app's theme rather than a guess.
  • Logos in Library. Connectors from the MCP catalogue show their brand logo, custom servers their first letter, GitHub imports GitHub's logo and each built-in helper its own mark. Library's icons have TalkBack labels.

Full changelog: v0.1.4...v0.1.5