Changelog
All notable changes to Reflick are documented here. Format follows Keep a Changelog; this project does not yet follow strict SemVer (pre-1.0 development history, first public tag will be v1.0.0).
This file is the curated, public-facing summary. The unabridged engineering history (every decision, every bug, every "why") is kept as private local dev notes, not part of this public repo.
[Unreleased]
Fixed
- The Realtime voice switch told you the wrong thing about money. It said a live conversation was billed by the minute, which was true when it was written and stopped being true when the charge became a flat three credits at the moment a session starts, however long you stay in it. It says that now, next to what an ordinary task costs and the reminder that talking normally is free
- A service warning from us no longer looks like an ordinary answer. Reflick can show you a short notice in the notch, and it carries a severity the sender chooses -- which was stored, sent, parsed, and then ignored, so "we are having a problem right now" arrived looking exactly like "the fix you asked about has shipped". A warning is amber now, and stays until you dismiss it
- The notch admits when Reflick is still working on something you have stopped watching. A task you started, then spoke again over, kept running and went silent: the only way to find out was to hover a notch that was giving you no reason to hover. A small mark appears beside it now, and only when nothing else is already saying so
- The default cursor is a proper pointer now, not a triangle. It is the shape the brand is named after, it carries a white edge so it stays visible over anything, and it is the same silhouette the installer window draws
- Dictation can learn your own names and jargon. Reflick has been assembling a list of the words it keeps hearing from you for a long time and was throwing every one of them away before it reached the transcriber. It is a switch in Settings, Voice, because it trades the multi-language listening that handles accents well, and that trade is not the same answer for everybody
- Dictation into a text field typed nothing at all. Releasing the key asked the transcriber for your words and then, without waiting for them to arrive, typed the empty result. The words turned up a moment later with nothing left to put them anywhere
- Settings sat on top of every other window, forever. Open it, click your browser, and Settings stayed in front of it. Same for the teaching board, which is worse because it is meant to stay open while you work
- Reflick never checked for updates. Everything needed to ship a fix was in place except the one line that makes an installed copy look, so a bug could be fixed in a release you would never be offered
- Dictation was slow after a pause. The access token it needs lasts about a hundred seconds, so any press after a gap waited for a new one before it could hear you. It now stays renewed for ten minutes after you last spoke
- Five buttons showed the wrong cursor. Three of them were on the screen where you sign in to restore credits you had paid for, where an arrow instead of a pointing hand reads as the button being dead. The others were the appearance card in setup and the send button in the text window
- A run whose answer stopped halfway charged you for it. If the connection to the model dropped after the reply had started arriving, the credit was taken and never given back, and what reached the app was a half-sentence it could not read. The credit now comes back, which is what was already true for every other way a run can fail
- Reflick would say it had done something without having done it. A reply like "I've opened Safari and searched for that" was taken at its word and recorded as a success, even when nothing had been clicked. It now checks its own record of what it did before claiming a job is finished, and says so plainly when the two disagree
- Anything irreversible now asks first, whichever way you phrase it. "Delete file notes.txt" asked before deleting; "delete the notes file" did not, because the two took different routes through Reflick and only one of them had the question. Sending mail, sending a message, replying, archiving, deleting a file, emptying to trash and changing or deleting a calendar event all ask, both ways
- Sixty-one things Reflick already knew how to do for free were being paid for. "Pause music", "next song", "battery level", "what tabs are open", "when's my next meeting" and fifty-six more had an instant local answer that the app could not reach, so each one bought a round trip to a model and could come back as small talk, doing nothing at all
- The appearance you chose was never asked for and could not be changed. Reflick ships with two looks, Professional and Buddy, and it drew everything in Professional because nothing anywhere ever asked which you wanted. It asks during setup now, and it is in Settings, General
- Two file pickers opened behind the window that opened them. The Skills import and export panels are modal, so the Cancel button ended up behind the window you were looking at with no way back
- "Open YouTube and play the first video then tell me what happens" was treated as one instruction rather than three, so it was never planned as the multi-step job it is
Added
- The notch splits into two panels while several jobs run: what the current one is working through on the left, the others as coloured tags on the right, divided by a hairline. One job still draws one panel, because a divider with nothing beside it promises content that never arrives
- The RefAI page now shows the calibration curve rather than claiming one. Eight buckets, what it said, what it actually got right, and the caveat that all of it is measured on phrasing we wrote ourselves
- RefAI is on. Reflick now works out what you meant on your own Mac for four out of five things you say, in a fiftieth of a millisecond, without sending anything anywhere -- and hands on the rest, which costs what it costs today. It can never decide to DO something to your Mac; that is a hard rule and not a confidence threshold, so the worst it can get wrong is spending a turn it would have spent anyway. There is a switch in Settings, Voice
- The admin console now says whether it is safe to require a registered install. Reflick has been allowing requests from builds that predate per-device accounts, which is the last way to use the service without one, and nothing could tell you whether any were left -- so the switch that closes it stayed off because nobody could say it was safe, rather than because it was not
- The other jobs Reflick is running arrive one after another in the notch rather than all at once, which is what makes a row of coloured tags read as several things happening instead of one block appearing. Off if you have asked macOS to reduce motion
- RefAI, Reflick's own on-device decision layer, now actually runs. It works out what you meant on your Mac, in a fiftieth of a millisecond, without calling anything -- and when it is not sure, it says so and steps aside rather than guessing. It is off until you turn it on in Settings, because the evidence behind it is measured on phrasing we wrote ourselves
- While Reflick is working on one thing, the other things it is working on are visible. Several tasks can run at once and only the one you last spoke to was ever on screen; the rest now appear as small coloured tags under the progress bar, each a different colour, with a count when there are more than fit
- Reflick's own pages can be found now. Crawl directives, a sitemap, and the structured data a search result or a voice assistant reads a sentence out of. A separate file tells anything summarising Reflick what is *not* true about it, because the likeliest wrong answer about a product is one nobody thought to deny
- A CAPTCHA on the admin console, off until it is switched on. Everything already guarding that page makes a single *guess* expensive; none of it touches the *address*, so a rate limit that gives one network eight tries gives a thousand networks eight thousand. The challenge is checked before the password is hashed, and a challenge that cannot be reached is refused rather than waved through
- A page for RefAI, the part of Reflick that decides what you meant before anything is paid for, with a waitlist for early access and for an API key. Every number on it is one we measured, the latency figure says out loud that it is a floor rather than a benchmark, and it states plainly that RefAI is not a trained model yet
- The people who make Reflick can now tell you something without shipping a new version: a short notice in the notch, shown once, that expires on its own. Used for things a release cannot say, like a service problem or a fix landing\n- Reflick tells you when there is a new version, in the notch. It checks once a day on its own. It never replaces itself without asking, because it holds the Accessibility grant and swapping the binary under a running task is not something to do quietly
- The notch now shows what the agent is actually working through: the steps it planned, with the one it is on picked out and the finished ones ticked off. Reflick has been planning multi-stage jobs for a long time and had never shown anybody the plan
- An admin console at admin.reflick.my. Accounts, balances, plans, how many Macs are on an account and when it was last used, plus the waitlist. You can grant or revoke credits, cancel a plan and delete an account, and every one of those is written down before it happens
- Two agents running at once are now drawn in different colours, so the one you just started is tellable from the one still finishing
- A proper installer window. Reflick's eight buddies in their own colours, each with its pointer, around the drag-to-Applications arrow
- Terms of Use, written from what the app actually does, including what you are responsible for when the agent drives your Mac and what your 14-day right to change your mind covers
- The morning brief could be prepared but never switched on. Reflick could compose one, check it was safe to run, and hand it to you, but nothing anywhere could put one in the diary, so none of that could ever happen. There is a card in Settings, Skills now: pick a time, see what it costs before you agree to it, and turn it off again. It says up front that a daily job is about thirty credits a month, tells you when your balance will not cover the month, and warns you that an overnight run on battery will not happen because a sleeping Mac runs nothing
- A knowledge base you can read. Everything Reflick remembers, written as plain markdown you can open in Finder, and an erase button that removes all of it. The erase half had been written for a while and was reachable from nowhere
- Reflick notices a task you repeat. Three times on the same weekday around the same hour and it says so, once, in a panel you opened. It offers a sentence, never a button that runs something
- Dictation writes as well as types. Look at an email and say "reply that Tuesday works" and it writes the reply; say "Tuesday works" and those words are typed, exactly as before
- The first screen says what Reflick is, and shows the four gestures, instead of opening with a permissions checklist over an empty window
- Streaming speech: playback starts on the first chunk rather than the finished clip. Measured against the live service, a long answer now begins in 0.40s instead of 3.97s, and the wait no longer grows with how much there is to say
- Realtime voice can see the screen. A screenshot goes up when you start speaking, so the fastest mode is no longer the blind one
- Live hover highlighting: hold the talk key and Reflick outlines the control under the pointer and names it, from the real Accessibility frame rather than an estimate
- Whole-document reading. "Summarise this" now answers from the whole file rather than the page that happens to be visible
- A skill library covering the apps a Mac user is in all day, with the shortcut-backed ones running instantly and no model call at all
- Skills can be shared: import a file somebody sent you, or export your own. A shared skill can explain how something works and can never press a key
- Teams. Several Macs share one account and one balance, joined with a short-lived single-use invite code
- Monthly subscriptions and one-off credit packs, paid through Revolut, with the checkout hosted by the service itself
- A self test in Settings, Permissions, that says which of several identical-looking failures you are actually hitting, and what to do about each
- This changelog, served publicly
- Reflick draws by hand. Hold the talk key and the control under the pointer is circled with a marker loop that draws itself, and a walkthrough's ring and arrow are drawn the same way, with a head made of two flicks rather than a filled triangle. The geometry underneath is unchanged and is still the real Accessibility frame, so the line lands on the control rather than near it
- Reflick knows which WEBSITE you are on, not just which browser. "Google Chrome" was one bucket holding your mail, your issue tracker and your bank, so nothing it knew could be specific to any of them. Skills can now be written for a site, and the ones that ship cover mail, documents, calendars, code hosting, issue tracking, video and design
- "Summarise this" works on a web page. It already read a whole PDF rather than the visible page; a web page is the document most people look at all day and it was the one it could not read. When a page is too large to read inside its time budget, the answer says it read the beginning rather than pretending it read all of it
- You can drag a file onto Reflick's menu bar icon and then talk about it. "Summarise this", "make these a thousand pixels wide". Everything needed to do those things already existed and there was no way to hand it a file
- Teaching shows a border. Both ways of teaching Reflick were completely invisible while they ran, and one of them quietly turns your next ordinary command into a step of the thing being taught, so the border names what is being recorded and says how to stop
- The self test answers two more questions: whether Reflick can tell which website you are on, and whether it can read the whole of what you are looking at. Both fail silently by design, so a thin answer had no explanation anywhere
Fixed
- Auto-update was switched off, and not dormant. The update framework is started at launch, so without its signing key it failed on every launch, and anyone who downloaded a build with a bug in it would have had no way to ever receive the fix
- The agent's own activity log grew forever. Five weeks of use had reached fourteen thousand lines and nearly two megabytes, on a file nothing reads and nothing offered to clear. It now keeps roughly a fortnight
- Reflick asked the system which website you were on three separate times for every single question, when once is enough
- Sign in with Apple could never have worked, for two separate reasons, and neither was visible from a passing build. The app asked Apple to embed a hashed value in the identity token and then sent the service the unhashed one, so the service compared two things that can never match and refused every sign-in with an authentication error. And the whole feature depended on an entitlement that Apple only grants through a provisioning profile, which a Mac app distributed outside the App Store cannot obtain. Sign in with Apple now runs through Apple's browser flow, which needs no entitlement, and the token check is exercised by tests that fail if either bug comes back
- The disk image was not signed. Two signing certificates with the identical name existed on the build machine, so the signing tool refused to choose between them and signed nothing, and because its complaint was routed through another command the build reported success anyway. The check that was supposed to catch this inspected the app rather than the image, and the app is signed separately, so it always passed. The build now resolves the certificate to one exact fingerprint, and a disk image that cannot be signed stops the build instead of shipping
Security
- "Erase everything Reflick has stored" did not erase everything. It deleted a hand-written list of ten files, and the log of every action the agent had ever taken was not on that list and never had been -- nearly two megabytes of history left behind after the button said it was gone, along with the readable copy exported as markdown. It now empties Reflick's own folder rather than a list, so anything added later is covered on the day it is written instead of when somebody remembers to add it
- Two ways a payment could be taken without the runs it paid for. The record that stops a payment being counted twice was written in a separate step from the credit itself, so a failure in between left the payment marked as handled and the balance untouched, and every retry afterwards reported success. It could happen on a one-off purchase and on a monthly renewal, where the money has already left the account before the record is written. Both now happen as a single transaction: either the payment is recorded and the runs are there, or nothing happened and the next attempt tries again
- The signature check that decides whether a payment notification is genuine had no test covering it. The tests exercised a second copy of the same logic, so the real check could have been weakened or deleted with every test still passing. It is now tested directly, verified by deleting its safeguard and confirming the tests fail
- The cap on free accounts per network per day was not being enforced. The service ships a documented limit of five, chosen to leave room for a household or an office behind one address, but a deployment setting was silently overriding it, and seven accounts in a row were provisioned from a single address in testing. The limit now lives with the reasoning that justifies it rather than in an invisible setting, and the one job that legitimately needs to create accounts, the live test suite, identifies itself with its own server-only secret instead of the limit being lifted for everybody. That secret is absent by default, so a deployment that forgets it locks the test out rather than opening the door
- Every response from the service now carries the standard set of protective headers, applied at the single point every response passes through rather than at each individual route, so a route added later cannot quietly miss them. The service serves exactly one page, the checkout, and a checkout that cannot be framed cannot be placed inside a page pretending to be something else
- The website no longer permits inline scripts. Its two scripts are now allowed by cryptographic hash, so a script injected into the page does not run
Fixed
- Realtime voice never worked, for four separate reasons, each hidden behind the one in front of it: a retired API header that closed the socket after a successful handshake, a failed state that could never be retried, a microphone that was never requested, and a release branch that reported an error over a working session
- The menu bar icon had never drawn anything. The status item was there and clickable the whole time
- The panel was centre-clipped, losing about twenty points off both ends of every row, because one over-wide row sized the whole stack
- The mascot sat on top of the answer, the thinking dots and the waveform
- Credits could show a balance for an account the service did not have
- Dictated text arrived carrying the modifiers of the shortcut being held, so nothing appeared
- Circling something now starts listening, instead of waiting for a second gesture nobody knew about
- Realtime voice had still never opened on real hardware. Switching on echo cancellation succeeded and then made the audio engine refuse to initialise, which surfaced a second after every keypress as "sorry, that didn't work" — and looked from the outside like the setting turning itself off. It now drops echo cancellation rather than the session, and holds the microphone shut while the model speaks so it cannot answer its own voice
- Talking was not free. Pointing at something on screen is a single-shot call that carries the computer tool and no conversation to continue, so the service could not tell it apart from an agent task starting and charged a credit for it — while pointing runs alongside ordinary conversation. A balance could drain without a single task being run
- "Get more" opened a checkout page that could only say the link was missing its account, whenever the balance had not loaded yet
- The notch never went back in after an answer. Every path that ends in something specific handed the island back; the ordinary spoken answer, which is most of them, did not — so the only reliable way to get the top of the screen back was for something to go wrong
- Escape cancels circling, in both halves of it. The overlay could never take keyboard focus, and once it closed the microphone stayed open with no stop of any kind
- The intent classifier ran on the frontier model to return one word from a fixed list, ahead of every request. It now runs on the small one
- The first question after launch paid for ScreenCaptureKit's one-off connection. That handshake now happens at startup, and only when screen recording has already been granted
[Unreleased] — public release preparation
Added
ARCHITECTURE.md— full system architecture with diagrams, aimed at a new contributorCONTRIBUTING.md,CODE_OF_CONDUCT.md,SECURITY.md, thisCHANGELOG.md- GitHub issue templates, pull request template, label set, discussion category templates
Changed
- README rewritten from scratch for a first-time developer audience
Fixed
- Real deployed Worker URL, present in
AGENTS.md/CLAUDE.mdand a code comment, redacted to a placeholder before public release - Removed a debug-only presentation-bypass flag (
CompanionManager) left over from a prior sprint's manual validation pass - Corrected a stale doc comment in
OpenAIAgentOrchestrator.swiftthat still described the completion-verification step as an "always-passes placeholder" after it had already been replaced with real logic
[1.0.0] — first public release
The full history below is condensed from ~90 commits across nine internal development sprints. Grouped by theme, not by commit.
Core voice pipeline
- Push-to-talk voice capture with streaming transcription (Deepgram default; AssemblyAI, OpenAI audio, and on-device Apple Speech as fallbacks)
- Deterministic intent classification (
ReflickIntent) routing every transcript to conversation, pointing, execution, or "unsupported" — replacing an earlier design that required a spoken trigger phrase - Streaming, sentence-chunked text-to-speech (Cartesia, with on-device
AVSpeechSynthesizerfallback) so responses start speaking before the full answer is ready - A unified voice-session state machine (idle/listening/transcribing/speaking/interrupting) and push-to-talk interruption of in-progress speech
Agent Runtime (computer control)
- A real state machine (
AgentTask/AgentTaskStateMachine) driving every command through received → planning → executing → verifying/recovering → a terminal state AgentPlanning,AgentExecuting,AgentVerifying,AgentRecoveringprotocol seams, each with an honestly-scoped V1 implementation (seeARCHITECTURE.md's Honest limitations section for exactly what each one does and doesn't do yet)- Structured status reporting, cancellation, loop detection, latency/cost tracking, and structured logging throughout the loop
- Two hardening passes fixing real bugs found during manual validation: mid-batch cancellation propagation, and a recovery baseline that had been comparing against the wrong screenshot
Execution Router, Tool Registry, Policy Engine
- A provider-agnostic Tool Registry cataloguing native macOS capabilities, MCP tools, and the computer-use fallback behind one interface
- An Execution Router that tries a taught workflow, then a capability chain, then the volume domain, then a deterministic native-tool match, before ever falling back to the full vision-driven agent loop — skipping the expensive path whenever a cheap, reliable one exists
- A Policy Engine enforcing an allow/deny decision ahead of every tool invocation
- 19+ native macOS capabilities: clipboard, file operations, volume/brightness/Wi-Fi/Bluetooth/battery reads, app control, notifications, and more
Conversational & vision features
- Screen-aware conversational answers (screenshot + transcript sent to a vision model on every conversational turn)
- Accurate on-screen pointing ("point at the Save button"), snapping to the exact UI element via the Accessibility tree when available
- Region-select + ask — a read-only mode that never takes control
- Teach mode — record a demonstration once, replay it by name via the Workflow system
Onboarding, Settings, permissions
- A 10-screen first-run setup wizard (provider selection, Worker connection with live validation, voice setup, and all three permission grants)
- A full Settings window (General, Models, Voice, Providers, Keyboard, Developer, Permissions) — every secret Keychain-backed, editable anytime, no config-file editing required after first run
- A shared permission system (Accessibility, Screen Recording, Microphone, Screen Content) surfaced consistently across onboarding and Settings
Provider architecture
- Migrated the autonomous computer-control loop and on-screen pointing from Anthropic's Computer Use tool to OpenAI's computer-use tool — Anthropic is no longer used anywhere in the app
- Introduced a
ProviderRegistryabstraction so every provider category (transcription, conversational vision, computer-use vision, text-to-speech) is swappable behind a protocol, with a Cloudflare Worker proxy holding every real API key server-side
Security
- Removed hardcoded Worker URL and client token from source, moved to a gitignored local config file with Keychain-backed override
- Scrubbed a previously-compromised client token from git history
- Fixed a real data leak where an onboarding form field was posting to a third party's form backend instead of nowhere/nothing