<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcell Havlik</title>
    <description>The latest articles on DEV Community by Marcell Havlik (@todoforai).</description>
    <link>https://dev.to/todoforai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4137330%2F6004a8ed-c99f-4c91-95e3-e7af8178c406.png</url>
      <title>DEV Community: Marcell Havlik</title>
      <link>https://dev.to/todoforai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/todoforai"/>
    <language>en</language>
    <item>
      <title>Claude Code for non-coders and non-coding tasks: what works, what doesn't, and what to run for the rest</title>
      <dc:creator>Marcell Havlik</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:59:21 +0000</pubDate>
      <link>https://dev.to/todoforai/claude-code-for-non-coders-and-non-coding-tasks-what-works-what-doesnt-and-what-to-run-for-the-3lmh</link>
      <guid>https://dev.to/todoforai/claude-code-for-non-coders-and-non-coding-tasks-what-works-what-doesnt-and-what-to-run-for-the-3lmh</guid>
      <description>&lt;p&gt;The Reddit threads asking "anyone using Claude Code for non-coding tasks?" are long because the answer is yes, with caveats nobody puts in the title. We use Claude Code every day. This is a straight account of what it does well outside the repo, where it stops, and what we run next to it. Facts checked against Anthropic's docs on 2026-09-21.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; TODO for AI competes with Claude Code for part of the same budget. Where it beats us is stated. Our own case is confined to the last section.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works
&lt;/h2&gt;

&lt;p&gt;Claude Code is an agent with a shell, a file system and MCP, reachable from a terminal, an IDE, a desktop app and claude.ai/code. Anything that is a file, a command or an MCP server is fair game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email triage and drafts.&lt;/strong&gt; With the Gmail MCP server wired, it reads the inbox, labels, drafts replies. For a founder inbox this is the single most useful non-code job it does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calendar prep.&lt;/strong&gt; Google Calendar plus Slack plus Notion MCP: a "prep me for tomorrow" command that gathers context per meeting while you do something else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reports and documents.&lt;/strong&gt; Anything that starts from files or an API. Weekly numbers from a CSV, a changelog from git log, a summary from meeting transcripts. This is its home turf and it is very good here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data chores.&lt;/strong&gt; Renaming, reformatting, merging spreadsheets, deduping a contact list. Faster than writing the script yourself, because it writes the script.&lt;/p&gt;

&lt;p&gt;The common thread: the data is reachable from a shell on the machine Claude Code is running on, or from an MCP server it has been given.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it breaks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Browser work needs you at the machine.&lt;/strong&gt; Anthropic ships the Claude in Chrome extension, so Claude Code can open tabs and drive your logged-in session for the CMS admin, the ad account or the supplier portal. It works, and it pauses for you on logins and CAPTCHAs. But it needs a direct Anthropic plan and Chrome running next to the session, on your own machine. A cloud session runs on Anthropic's infrastructure and has no path to your local Chrome, so the browser half of your work is the half you have to be present for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unattended work is repo-anchored.&lt;/strong&gt; This is the caveat that moved. Cloud sessions and &lt;a href="https://code.claude.com/docs/en/routines" rel="noopener noreferrer"&gt;routines&lt;/a&gt; do run on Anthropic's infrastructure while your machine is off, triggered by a schedule, an API call or a GitHub event, and they can call your MCP connectors, writes included. But a routine is configured around GitHub repositories and a cloud environment whose network is allow-listed by default, and it pushes to &lt;code&gt;claude/&lt;/code&gt;-prefixed branches. Nightly PR review: excellent. Nightly "work the inbox and update the ad budgets": possible through connectors if every tool you need has one, impossible for anything that only exists behind a login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP tax.&lt;/strong&gt; Every SaaS tool without a CLI is a server to install, authenticate and keep working. Gmail, Calendar, Slack, Notion: four setups before the "chief of staff" demo works. Fine once; painful across a team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One vendor.&lt;/strong&gt; Claude only. When Claude is the wrong model for a job (cheap bulk classification, a second opinion on its own output), there is no switch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still developer-shaped.&lt;/strong&gt; The desktop app and claude.ai/code removed the terminal requirement, but the surface is still repos, branches and diffs. The person on your team who would benefit most from email triage is not going to connect a GitHub repository to get it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we run next to it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://todofor.ai" rel="noopener noreferrer"&gt;TODO for AI&lt;/a&gt; is built around the gaps above and not around the strengths: Claude Code's terminal workflow is more mature than ours and its code output is excellent, and the numbers, including where we score higher and why that is not a like-for-like comparison, are on &lt;a href="https://todofor.ai/vs/claude-code" rel="noopener noreferrer"&gt;the comparison page&lt;/a&gt;. The setup that works for us: Claude Code stays open for the repo. Everything that touches an inbox, a browser or a schedule goes on the TODO list, where it runs on a persistent cloud VM or a paired machine, on whichever model fits, from the web or desktop app. Browser items still need Chrome open on your side; what that reaches is in &lt;a href="https://todofor.ai/blog/ai-agent-logged-in-browser" rel="noopener noreferrer"&gt;the extension post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;TODO for AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fix the bug, open the PR&lt;/td&gt;
&lt;td&gt;Excellent, most mature workflow&lt;/td&gt;
&lt;td&gt;Yes, on your PC or its VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email triage via MCP&lt;/td&gt;
&lt;td&gt;Yes, after setup&lt;/td&gt;
&lt;td&gt;Yes, via your browser or MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post to the CMS, run ads, update the CRM&lt;/td&gt;
&lt;td&gt;With Claude in Chrome or an MCP connector, while you are there&lt;/td&gt;
&lt;td&gt;With our Chrome extension or a CLI, while you are there&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unattended overnight&lt;/td&gt;
&lt;td&gt;Yes: repo-anchored cloud session plus connectors&lt;/td&gt;
&lt;td&gt;Yes, on a persistent VM; browser items wait for Chrome&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switch models per task&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-developer can use it&lt;/td&gt;
&lt;td&gt;Not really: repos, branches, diffs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your non-coding tasks are files, APIs and the occasional browser job while you sit there, Claude Code already does them. If the unattended half of your work is not a repository, or you want a second vendor's model reading the first one's output, that is what TODO for AI is for.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>aiagents</category>
      <category>productivity</category>
      <category>comparison</category>
    </item>
    <item>
      <title>How an AI agent works in your logged-in browser: extension vs CDP relay vs cloud browser</title>
      <dc:creator>Marcell Havlik</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:54:15 +0000</pubDate>
      <link>https://dev.to/todoforai/how-an-ai-agent-works-in-your-logged-in-browser-extension-vs-cdp-relay-vs-cloud-browser-40m5</link>
      <guid>https://dev.to/todoforai/how-an-ai-agent-works-in-your-logged-in-browser-extension-vs-cdp-relay-vs-cloud-browser-40m5</guid>
      <description>&lt;p&gt;Plenty of the work a business does in a browser has no usable API: the ad manager's bulk editor, the CMS admin, the supplier portal, the bank. If an agent is going to do that work, it needs a browser that is logged in as you. There are three ways to get one. They are not equivalent.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Cloud browser
&lt;/h2&gt;

&lt;p&gt;A headless Chrome on someone else's server. Browserbase, Firecrawl, Manus and most "computer use" demos run this way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach:&lt;/strong&gt; anything public. For your accounts it starts logged out, so you either paste credentials into a vendor's machine, sync cookies to it, or run a login flow with 2FA each time. Some vendors persist the session after the first login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaks:&lt;/strong&gt; your session lives on their infrastructure. Whatever their retention policy says, that is where the cookie is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stops at:&lt;/strong&gt; anything that fingerprints a datacenter IP, anything that wants your device (passkeys, hardware 2FA), and any site whose terms forbid automation from an unknown machine.&lt;/p&gt;

&lt;p&gt;Good for scraping, research and parallel jobs with no personal login. For "reply to the three support tickets in my Zendesk" it works only once your Zendesk session lives on their server.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. CDP relay to your local Chrome
&lt;/h2&gt;

&lt;p&gt;Chrome DevTools Protocol is the wire Chrome exposes for debugging. Start Chrome with &lt;code&gt;--remote-debugging-port&lt;/code&gt;, forward that socket to a server, and a remote agent drives that Chrome. This is how Playwright, Puppeteer and most "connect to my browser" tools work.&lt;/p&gt;

&lt;p&gt;One catch people hit: since Chrome 136 the debugging flags are ignored for the default profile directory, so &lt;code&gt;--remote-debugging-port&lt;/code&gt; alone gives you a &lt;em&gt;fresh&lt;/em&gt; profile with none of your logins. Getting your real sessions means copying the profile to a &lt;code&gt;--user-data-dir&lt;/code&gt; you pass explicitly, or going through an extension instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach:&lt;/strong&gt; with your profile attached, everything you can reach. Same cookies, same extensions, same device for passkeys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaks:&lt;/strong&gt; the relay sees every page, every DOM, every keystroke it sends. Whoever holds the relay endpoint holds your browser. The token in that URL is a full-session credential and has to be treated like one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stops at:&lt;/strong&gt; Chrome not running. Raw CDP on the debugging port is also all-or-nothing: the protocol has no "this tab only" or "read but do not type", and &lt;code&gt;Network.getAllCookies&lt;/code&gt; returns the whole jar, so any scoping has to be built on top of it.&lt;/p&gt;

&lt;p&gt;Powerful, and the right answer when the agent already runs on your machine. Risky when the agent runs on a vendor's machine and the relay goes across the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Browser extension
&lt;/h2&gt;

&lt;p&gt;Code that runs inside your Chrome with the permissions the extension manifest declares. The agent sends intents; the extension executes them locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach:&lt;/strong&gt; whatever the extension's code lets through, on the tabs it is allowed to touch, and whatever its manifest permissions allow. An extension that requests &lt;code&gt;cookies&lt;/code&gt; can read cookies; one that requests &lt;code&gt;debugger&lt;/code&gt; gets the same CDP surface as a relay, scoped to the tabs it attaches to. It runs as you, on your device, so logins and device-bound 2FA are available if the extension exposes them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaks:&lt;/strong&gt; page content goes wherever the extension sends it. A well-built one sends the accessibility tree or a screenshot of the working tab, not your whole browser. The scope is inspectable: it is in the manifest and in the Chrome permissions dialog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stops at:&lt;/strong&gt; the same place a CDP relay does. Chrome has to be running, so nothing happens while your laptop is closed. Chrome also blocks extensions on browser-internal pages and the Web Store.&lt;/p&gt;

&lt;p&gt;Narrower than raw CDP if it is built that way. That is the feature, and it is the extension author's choice, not the transport's.&lt;/p&gt;

&lt;h2&gt;
  
  
  How TODO for AI does it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://chromewebstore.google.com/detail/todo-for-ai/oemlbhbggllbelfemliboclfagbchcoj" rel="noopener noreferrer"&gt;Chrome extension&lt;/a&gt; is a hybrid of 2 and 3, and it is worth being precise about which parts are which.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CDP-shaped commands, through the extension.&lt;/strong&gt; The agent speaks a CDP subset over a WebSocket to our API, and the extension executes it. Two modes, and the difference matters: the default shim mode serves a fixed list of page-level methods (DOM, Accessibility, Input, &lt;code&gt;Runtime.evaluate&lt;/code&gt;, screenshots) by injecting an engine into the page, with no debugger banner and no browser-level domains; debugger mode forwards commands to Chrome's &lt;code&gt;debugger&lt;/code&gt; API for full fidelity, banner included, and then the reachable surface is CDP itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attached tabs only.&lt;/strong&gt; You click "attach" on a tab in the extension popup. The agent can control those tabs and any new tab it opens itself. A command aimed at an unattached tab is refused in both modes. Debugger mode plus the explicit allow-unattached setting lifts that, and then an agent that names a tab id can reach any tab. Chrome internal pages and the Web Store cannot be attached at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookies: no extension cookie API, but do not read that as a boundary.&lt;/strong&gt; The extension does not request the &lt;code&gt;cookies&lt;/code&gt; permission. In shim mode the agent reads what a script on the attached page could read, &lt;code&gt;document.cookie&lt;/code&gt; included. In debugger mode CDP goes to Chrome as-is, and the &lt;code&gt;Network&lt;/code&gt; domain is broader than one tab: &lt;code&gt;Network.getAllCookies&lt;/code&gt; is a whole-browser read. The boundary is the mode you pick and the permission you give the agent, not the missing &lt;code&gt;cookies&lt;/code&gt; permission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page content crosses the wire.&lt;/strong&gt; Snapshots, DOM and screenshots of attached tabs go to the API so the agent can see them. That is the leak surface. The attach list bounds it, unless you turn on allow-unattached in debugger mode, which lets an agent name any tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-level permissions and a stop button.&lt;/strong&gt; Browser access is a capability you set per agent to allow, ask-first or block, and the default for a new agent is allow. Ask-first gates each tool call, not each outcome, and the shell is a separate tool that can also reach a browser, so gate both if that matters. Any run can be halted mid-task; the tab stays where it was.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud browser for the rest.&lt;/strong&gt; Research that needs no login runs in a per-account cloud browser, so your Chrome is free and your laptop can be closed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it does not do: run while Chrome is closed. There is no per-site block list and no read-only mode; the attach list, the mode, and the per-agent permission are the scope controls. If a site must never be reachable, do not leave it attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one, in one table
&lt;/h2&gt;

&lt;p&gt;The last column is our extension, not extensions in general; another extension can make different choices.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloud browser&lt;/th&gt;
&lt;th&gt;CDP relay&lt;/th&gt;
&lt;th&gt;TODO for AI extension&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Uses your existing logins&lt;/td&gt;
&lt;td&gt;Only after you hand them over&lt;/td&gt;
&lt;td&gt;Yes, with your profile attached&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passkeys, hardware 2FA&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, you are at the device&lt;/td&gt;
&lt;td&gt;Yes, you are at the device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What crosses the wire&lt;/td&gt;
&lt;td&gt;The whole session&lt;/td&gt;
&lt;td&gt;Everything in the browser&lt;/td&gt;
&lt;td&gt;Page content of attached tabs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope control&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;td&gt;Attached tabs, shim vs debugger mode, per-agent permission&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs while laptop closed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No, Chrome has to be running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter IP blocks&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the task needs you, use the extension. If it needs nobody, use the cloud browser. A relay is for when the agent is already on your machine and you trust every hop. What this means for a to-do list that actually closes items is in &lt;a href="https://todofor.ai/blog/ai-todo-list-that-does-the-tasks" rel="noopener noreferrer"&gt;the to-do post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>browseragent</category>
      <category>chromeextension</category>
      <category>aiagents</category>
      <category>security</category>
    </item>
    <item>
      <title>Our task categorizer routed 46% of tasks. A $0.04/MTok decision model routes 97%.</title>
      <dc:creator>Marcell Havlik</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:54:08 +0000</pubDate>
      <link>https://dev.to/todoforai/our-task-categorizer-routed-46-of-tasks-a-004mtok-decision-model-routes-97-7mf</link>
      <guid>https://dev.to/todoforai/our-task-categorizer-routed-46-of-tasks-a-004mtok-decision-model-routes-97-7mf</guid>
      <description>&lt;p&gt;&lt;em&gt;Cosine similarity between a task title and a group description is not a classifier.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Bench, cases, raw verdicts: &lt;code&gt;backend/bench/categorization&lt;/code&gt; in the TODOforAI repo. Total&lt;br&gt;
cost ~$0.50.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;TODOforAI boards have groups (&lt;em&gt;SEO&lt;/em&gt;, &lt;em&gt;Paid&lt;/em&gt;, &lt;em&gt;Frontend&lt;/em&gt;…); new tasks get a suggested&lt;br&gt;
group. V1: embed title and group name + description, nearest cosine wins. Embedder&lt;br&gt;
&lt;code&gt;Qwen/Qwen3-Embedding-4B&lt;/code&gt; via DeepInfra, 512 dims, unit-normalized, query instruction&lt;br&gt;
&lt;em&gt;"Given a task, retrieve the workstream whose purpose best matches the task"&lt;/em&gt;. Abstain if&lt;br&gt;
best &amp;lt; 0.50 or margin to runner-up &amp;lt; 0.06.&lt;/p&gt;

&lt;p&gt;100% precision on 18 hand cases. Most of my real board in Unsorted.&lt;/p&gt;
&lt;h2&gt;
  
  
  Judges
&lt;/h2&gt;

&lt;p&gt;Same 138 titles, same 8 groups, same description text, no example tasks — exactly what&lt;br&gt;
production sees. Titles written like real todos: short, half Hungarian, 15 nonsense&lt;br&gt;
(&lt;code&gt;call mom&lt;/code&gt;, &lt;code&gt;asdf&lt;/code&gt;), 5 ambiguous.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;embedding&lt;/strong&gt; — production, unchanged&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jev&lt;/strong&gt; — &lt;a href="https://vercel.com/ai-gateway/models/jev" rel="noopener noreferrer"&gt;TypeSafe Jev&lt;/a&gt;, non-autoregressive
decision model on Vercel AI Gateway. One &lt;code&gt;choice&lt;/code&gt; question, 8 groups + &lt;code&gt;none&lt;/code&gt;.
$0.04/MTok in, output free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sonnet&lt;/strong&gt; — Claude Sonnet 4.6, temp 0, one slug or &lt;code&gt;none&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;opus&lt;/strong&gt; — Claude Opus 5, same prompt. &lt;strong&gt;The truth.&lt;/strong&gt; My labels are one more opinion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trap: Opus 5 reasons before answering; &lt;code&gt;max_tokens: 20&lt;/code&gt; looked like abstaining on 88/138.&lt;br&gt;
Give it 400.&lt;/p&gt;
&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Judge&lt;/th&gt;
&lt;th&gt;agrees with Opus&lt;/th&gt;
&lt;th&gt;wrong group&lt;/th&gt;
&lt;th&gt;missed&lt;/th&gt;
&lt;th&gt;138 tasks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;embedding&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77/138&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60/112&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jev&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;130/138&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3/112&lt;/td&gt;
&lt;td&gt;24 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 4.6&lt;/td&gt;
&lt;td&gt;131/138&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0/112&lt;/td&gt;
&lt;td&gt;57 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Opus routed 112, called &lt;code&gt;none&lt;/code&gt; on 26 (15 nonsense + 11 vague). Embedding: wrong once,&lt;br&gt;
missed 54%. Jev: 97% routed, 5 wrong. Sonnet's 7 wrong are coin flips with Opus.&lt;br&gt;
On my real board Jev and Opus agree 21/26.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the embedding abstains
&lt;/h2&gt;

&lt;p&gt;Not the margin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         agree   wrong   missed
margin 0.06  floor 0.50    76      2       60     ← production
margin 0     floor 0.50    79     41       18
margin 0     floor 0       76     62        0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop the gate and Unsorted becomes wrong group. &lt;strong&gt;Top-1 is wrong on ~45% of tasks.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bun test flaky on CI          frontend 0.540   development 0.537
call mom                      email 0.540      plg 0.525
asdf                          development 0.663  frontend 0.659
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scores sit in 0.42–0.72; &lt;code&gt;asdf&lt;/code&gt; outscores a real task, so no floor finds nonsense.&lt;br&gt;
Frontend/Development, Paid/SEO, Enterprise/Paid overlap, so real tasks sit inside any&lt;br&gt;
margin. Jev and the LLMs know &lt;em&gt;outcomes&lt;/em&gt;: a CI flake is Development because of what fixing&lt;br&gt;
it achieves. Similarity can't say that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped
&lt;/h2&gt;

&lt;p&gt;Jev routes; embedding is fallback when the gateway is unset or down. Batches of 25,&lt;br&gt;
&lt;code&gt;none&lt;/code&gt; explicit, accept probability ≥ 0.5. ~0.3 s per task.&lt;/p&gt;

&lt;p&gt;Not Sonnet: 57 s vs 24 s and ~100× the price, for a suggestion 400 ms after you stop typing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Router&lt;/th&gt;
&lt;th&gt;per 1,000 tasks&lt;/th&gt;
&lt;th&gt;per 1M tasks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;embedding (~15 tok, $0.01/MTok)&lt;/td&gt;
&lt;td&gt;$0.0002&lt;/td&gt;
&lt;td&gt;$0.15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;jev&lt;/strong&gt; (~260 tok catalog per question, $0.04/MTok in, out free)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.01&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 4.6 (~350 in / 10 out)&lt;/td&gt;
&lt;td&gt;$1.20&lt;/td&gt;
&lt;td&gt;$1,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus 5 (~350 in / ~200 out with reasoning)&lt;/td&gt;
&lt;td&gt;$7&lt;/td&gt;
&lt;td&gt;$7,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;List prices, 8 groups. Jev costs ~70× the embedding and routes twice as much.&lt;/p&gt;

&lt;p&gt;Jev's 5 wrong are channel-vs-outcome (&lt;code&gt;sponsor a newsletter, measure signups&lt;/code&gt; → email,&lt;br&gt;
not paid). Confidence is lower when wrong (0.70 vs 0.94), but floor 0.6 trades 2 wrong&lt;br&gt;
for 7 Unsorted. We keep the 5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Precision alone lies. Print recall against something you trust.&lt;/li&gt;
&lt;li&gt;Score against a strong model, not your own labels.&lt;/li&gt;
&lt;li&gt;Check the reference's output budget. Truncated looks cautious.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;138 cases is small. It's enough to see a 54% recall gap; that's the only claim.&lt;/p&gt;

</description>
      <category>benchmarks</category>
      <category>categorization</category>
      <category>embeddings</category>
      <category>llm</category>
    </item>
    <item>
      <title>94.7% on LoCoMo — and why most of the gap between published memory numbers isn't the memory</title>
      <dc:creator>Marcell Havlik</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:46:31 +0000</pubDate>
      <link>https://dev.to/todoforai/947-on-locomo-and-why-most-of-the-gap-between-published-memory-numbers-isnt-the-memory-26bo</link>
      <guid>https://dev.to/todoforai/947-on-locomo-and-why-most-of-the-gap-between-published-memory-numbers-isnt-the-memory-26bo</guid>
      <description>&lt;p&gt;&lt;em&gt;94.7% on LoCoMo at 5.0K context tokens — 95.0% when re-graded by Mem0's own judge. The&lt;br&gt;
more useful finding is what we measured on the way there.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The number, with its configuration attached
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;94.7% on LoCoMo&lt;/strong&gt;, all 1540 questions, at &lt;strong&gt;5.0K context tokens per question&lt;/strong&gt;. It is&lt;br&gt;
the highest LoCoMo number we are aware of, published or otherwise, and we can hand you the&lt;br&gt;
code and the per-question outputs for it&lt;br&gt;
(&lt;a href="https://github.com/todoforai/livemem" rel="noopener noreferrer"&gt;github.com/todoforai/livemem&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The nearest published row is Mem0's April 2026 algorithm at&lt;br&gt;
&lt;a href="https://mem0.ai/blog/understanding-memory-benchmark-for-production-ai-agents" rel="noopener noreferrer"&gt;92.5, delivered in 6,956 mean context tokens&lt;/a&gt;;&lt;br&gt;
then MemMachine's 91.7% for v0.2. So: +2.2 points on about 30% less context.&lt;/p&gt;

&lt;p&gt;Per category: open-domain 97.0%, single-hop 93.6%, temporal 93.5%, &lt;strong&gt;multi-hop 81.2%&lt;/strong&gt;.&lt;br&gt;
That last one is where the remaining work is, and we'd rather print it than average it&lt;br&gt;
away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much of that is the grader?&lt;/strong&gt; This is the first question we would ask, so we&lt;br&gt;
answered it: we re-graded the identical 1540 answers with &lt;code&gt;gpt-4o-mini&lt;/code&gt; running Mem0's own&lt;br&gt;
&lt;code&gt;ACCURACY_PROMPT&lt;/code&gt; — the judge model and prompt behind their published LoCoMo numbers — and&lt;br&gt;
got &lt;strong&gt;95.0%&lt;/strong&gt; (+18 flips to correct, −13 the other way). The two judges agree within 0.3&lt;br&gt;
points on these answers. The lead is not a grading artefact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much of it is the ensemble?&lt;/strong&gt; The answers come from two passes over the &lt;em&gt;same&lt;/em&gt;&lt;br&gt;
retrieved memory: &lt;code&gt;gemini-flash&lt;/code&gt; and &lt;code&gt;claude-haiku-4-5&lt;/code&gt; answer the identical 5.0K block&lt;br&gt;
independently, then a rule hierarchy (agree &amp;gt; commit &amp;gt; abstain) settles the 82&lt;br&gt;
disagreements. One pass alone scores &lt;strong&gt;92.4%&lt;/strong&gt;. So +2.3 points are answer-side, bought with&lt;br&gt;
a second model call and ~6 seconds per question instead of one — not with more context.&lt;/p&gt;

&lt;p&gt;That distinction turns out to be the theme of this entire post.&lt;/p&gt;

&lt;p&gt;And before anyone else says it: &lt;strong&gt;LoCoMo at 94.7% is a nearly saturated benchmark.&lt;/strong&gt; Mem0&lt;br&gt;
&lt;a href="https://mem0.ai/blog/understanding-memory-benchmark-for-production-ai-agents" rel="noopener noreferrer"&gt;say so themselves&lt;/a&gt;&lt;br&gt;
— "older benchmarks like LoCoMo are less informative today," because they don't stress&lt;br&gt;
conflicting updates, identity ambiguity or long-running workflows. We agree, and we still&lt;br&gt;
publish the row, because a saturated benchmark is exactly the regime where the &lt;em&gt;unreported&lt;/em&gt;&lt;br&gt;
variables — answerer, judge, context budget — decide the ranking. Two points of headroom&lt;br&gt;
and a seven-point answerer effect is not a leaderboard; it's a measurement problem. The&lt;br&gt;
rest of this post is that measurement problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;We build &lt;a href="https://todofor.ai" rel="noopener noreferrer"&gt;TODOforAI&lt;/a&gt;, an agent that runs long-lived tasks on your&lt;br&gt;
machine. Long-lived means it has to remember: what you decided three weeks ago, what your&lt;br&gt;
deploy procedure is, which of two conflicting instructions is the newer one.&lt;/p&gt;

&lt;p&gt;So we built a memory system, and then we did the thing you're supposed to do: benchmarked&lt;br&gt;
it against published numbers instead of trusting our own demos.&lt;/p&gt;

&lt;p&gt;Two public datasets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LongMemEval_S&lt;/strong&gt; — 500 questions over long, multi-session chat histories. Single-session
recall, multi-session aggregation, temporal reasoning, knowledge updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoCoMo&lt;/strong&gt; — 1540 questions, heavier on temporal and multi-hop reasoning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both run through &lt;a href="https://github.com/vectorize-io/agent-memory-benchmark" rel="noopener noreferrer"&gt;AMB&lt;/a&gt;, an&lt;br&gt;
independent harness that fixes the dataset, the answering model and the judge for every&lt;br&gt;
memory provider it runs.&lt;/p&gt;

&lt;p&gt;LoCoMo went well from early on. &lt;strong&gt;LongMemEval was the humbling one&lt;/strong&gt;, and still is: our&lt;br&gt;
first honest full-dataset number was &lt;strong&gt;85.4%&lt;/strong&gt;, against Mem0's published &lt;strong&gt;94.4%&lt;/strong&gt;. We are&lt;br&gt;
at &lt;strong&gt;87.8%&lt;/strong&gt; today — better, still behind, and behind at 4.2K context tokens against their&lt;br&gt;
6.8K, which is the only part of that comparison we're happy with.&lt;/p&gt;

&lt;p&gt;That 9-point hole is what sent us looking for bugs, and the bugs turned out to be more&lt;br&gt;
interesting than the number. It is also why the rest of this post spends more time on&lt;br&gt;
LongMemEval than on the benchmark we lead.&lt;/p&gt;
&lt;h2&gt;
  
  
  Chasing it
&lt;/h2&gt;

&lt;p&gt;The obvious hypothesis is that their architecture is better. So we went looking for what&lt;br&gt;
we were doing wrong, and we found real things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deduplication was deleting evidence.&lt;/strong&gt; A question like "how many plants did I buy last&lt;br&gt;
month?" needs &lt;em&gt;every&lt;/em&gt; instance, not the top-k most similar ones. Our dedup merged "bought&lt;br&gt;
2 basil plants" and "bought 3 basil plants" — near-identical sentences — silently&lt;br&gt;
destroying an instance the count depended on. Making dedup number-aware (two facts with&lt;br&gt;
different digits are never duplicates, however similar the prose) was worth &lt;strong&gt;+4.3 points&lt;/strong&gt;&lt;br&gt;
on a 94-question validation subset (78.7% → 83.0%).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extraction was too tasteful.&lt;/strong&gt; Our prompt skipped things "an LLM already knows." Then a&lt;br&gt;
question asked &lt;em&gt;"what was that Borges quote you gave me?"&lt;/em&gt; — public knowledge, yes, but&lt;br&gt;
the specific thing &lt;strong&gt;we&lt;/strong&gt; said in &lt;strong&gt;that&lt;/strong&gt; conversation is not in any model's weights. The&lt;br&gt;
lesson generalizes: extraction should be greedy, because a fact never extracted can never&lt;br&gt;
be retrieved. Selectivity belongs to the selector, not the extractor. This fixed three&lt;br&gt;
questions on our subset that had been unanswerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some answers don't live in facts at all.&lt;/strong&gt; They live in a conversation turn that no&lt;br&gt;
summary preserved. Adding a retrieval configuration that mixes entity cards with 3-turn&lt;br&gt;
conversation-window excerpts alongside the distilled facts took the full 500-question run&lt;br&gt;
from &lt;strong&gt;85.4% to 87.8%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Real improvements, real points. Still not 94.4%.&lt;/p&gt;
&lt;h2&gt;
  
  
  The variable we hadn't controlled
&lt;/h2&gt;

&lt;p&gt;Here's what we should have checked on day one. We had been answering with the cheapest&lt;br&gt;
model we could — &lt;code&gt;gemini-flash-lite&lt;/code&gt; — deliberately, for the reason above.&lt;/p&gt;

&lt;p&gt;Mem0's published harness config uses &lt;strong&gt;&lt;code&gt;gpt-5&lt;/code&gt; as the answerer and &lt;code&gt;gpt-5&lt;/code&gt; as the judge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So we ran the control: same memory system, same extracted facts, same retrieval, same&lt;br&gt;
rendered context. Swap only the answering model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Config (94-question subset)&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;flash-lite answerer, flash-lite judge&lt;/td&gt;
&lt;td&gt;85.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;gpt-5 answerer&lt;/strong&gt;, flash-lite judge&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;92.6%&lt;/strong&gt; (+7.4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-5 answerer, &lt;strong&gt;gpt-5 judge&lt;/strong&gt;, same answers re-judged&lt;/td&gt;
&lt;td&gt;90.4% (−2.2)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;+7.4 points from the answerer alone.&lt;/strong&gt; And the judge is a free variable too — though&lt;br&gt;
note the stronger judge was &lt;em&gt;stricter&lt;/em&gt;, not more generous: it accepted two hedged answers&lt;br&gt;
we'd been denied and rejected four we'd been given.&lt;/p&gt;

&lt;p&gt;Be careful what this does and doesn't show. It does &lt;strong&gt;not&lt;/strong&gt; prove Mem0's 94.4% is "really"&lt;br&gt;
lower, or that the gap between two systems is entirely answerer choice — we never ran&lt;br&gt;
their system on our harness. What it shows is narrower and still useful: &lt;strong&gt;on the same&lt;br&gt;
memory, configuration differences of the size that separate published rows are worth&lt;br&gt;
about as many points as the memory architecture itself.&lt;/strong&gt; Our own comparable-ish number&lt;br&gt;
is 90.4%, still short of 94.4%. We just no longer believe the raw distance between two&lt;br&gt;
differently-configured rows is a measurement of anything.&lt;/p&gt;
&lt;h2&gt;
  
  
  What we actually learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Cross-harness memory numbers are not a ranking.&lt;/strong&gt; The clearest evidence isn't even&lt;br&gt;
ours: in the published literature Mem0 appears at &lt;strong&gt;94.4%&lt;/strong&gt; (self-reported) and at&lt;br&gt;
&lt;strong&gt;67.6%&lt;/strong&gt; (measured in a third-party paper). Same system, 27 points apart, because the&lt;br&gt;
harness, answerer, judge prompt and budget differ. Any table mixing sources — including&lt;br&gt;
the one in &lt;a href="https://github.com/todoforai/livemem" rel="noopener noreferrer"&gt;our repo&lt;/a&gt; — should be read as a&lt;br&gt;
collection of separately-configured experiments, not a leaderboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Judges are worth points, not decimals — and not only via the prompt.&lt;/strong&gt; Our&lt;br&gt;
single-pass answers scored 92.4% under &lt;code&gt;claude-haiku-4-5&lt;/code&gt; and 95.6% under &lt;code&gt;gpt-4o-mini&lt;/code&gt;,&lt;br&gt;
on near-identical judge prompts. Nothing about the memory changed between those two&lt;br&gt;
numbers; only which model read the same grading instruction. (On the ensemble answers the&lt;br&gt;
same two judges land within 0.3 points of each other — the size of the judge effect is&lt;br&gt;
itself configuration-dependent, which is the point.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Context size is a hidden axis.&lt;/strong&gt; We run at ~4–5k tokens per question, measured after&lt;br&gt;
rendering. Mem0's new algorithm reports its 92.5 at 7.0K and its 94.4 at 6.8K; the&lt;br&gt;
Hindsight run on the AMB leaderboard reports 43.6k context tokens per question — about 9×&lt;br&gt;
ours. Accuracy per token is a different ranking than accuracy, and only one of the two is&lt;br&gt;
usually reported. Credit where due: Mem0 do report mean tokens next to every score, which&lt;br&gt;
is more than most leaderboard rows offer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Our own most embarrassing finding was in our token accounting.&lt;/strong&gt; Our "5000-token"&lt;br&gt;
budget was actually delivering &lt;strong&gt;5742 real tokens&lt;/strong&gt;. Date prefixes (&lt;code&gt;- [2026-08-14]&lt;/code&gt;) are&lt;br&gt;
15 characters but &lt;strong&gt;10 tokens&lt;/strong&gt; — dates are token-dense — and across a full block that&lt;br&gt;
came to roughly 1.6k tokens we never charged for. After enforcing the real budget, delivered&lt;br&gt;
context dropped to 4182 tokens and the score on our subset dropped from 83.0% to 77.7%. A&lt;br&gt;
rerun with the budget raised so &lt;em&gt;delivered&lt;/em&gt; context matched the old 5742 recovered to&lt;br&gt;
&lt;strong&gt;81.9%&lt;/strong&gt; — so the drop was the missing context, not lost capability. The uncounted tokens&lt;br&gt;
had been buying real answers. If you don't measure delivered tokens, your budget is&lt;br&gt;
decoration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The dominant remaining error family is aggregation.&lt;/strong&gt; "How many X in total", "what&lt;br&gt;
percentage of Y". The instances are usually &lt;em&gt;in&lt;/em&gt; the retrieved context — the answerer just&lt;br&gt;
doesn't count them reliably. That's not fixed by retrieving harder. It's also a direct&lt;br&gt;
cost of our weak-answerer choice: flash-lite stresses retrieval honestly, but it fails&lt;br&gt;
arithmetic that a stronger model wouldn't. There's no free methodology.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where LoCoMo stops being enough
&lt;/h2&gt;

&lt;p&gt;Mem0's benchmark post is worth reading precisely because it argues against its own top&lt;br&gt;
row, and its list of failure modes matches ours: &lt;strong&gt;paraphrase overfitting&lt;/strong&gt; (systems pass&lt;br&gt;
when the query echoes the stored wording), &lt;strong&gt;temporal revision&lt;/strong&gt; (the user changed their&lt;br&gt;
mind and both facts are still in the store), &lt;strong&gt;identity ambiguity&lt;/strong&gt;, &lt;strong&gt;mixed-granularity&lt;br&gt;
scoping&lt;/strong&gt; (per-user vs per-project vs per-device), and &lt;strong&gt;scale&lt;/strong&gt; — benchmarks run over&lt;br&gt;
hundreds of events, production over millions.&lt;/p&gt;

&lt;p&gt;Their own numbers show the drop: 92.5 on LoCoMo and 94.4 on LongMemEval_S, but &lt;strong&gt;64.1 on&lt;br&gt;
BEAM 1M and 48.6 on BEAM 10M&lt;/strong&gt; — same system, same year, thirty to forty-five points lower&lt;br&gt;
as soon as the benchmark contains interference, updates and long-horizon chains. That is a&lt;br&gt;
much more honest picture of where memory actually is than any 9x.x% row, including ours.&lt;/p&gt;

&lt;p&gt;We have not run BEAM yet. It's next, and we'll publish it the same way — with the answerer,&lt;br&gt;
the judge, the delivered token count and the per-question outputs attached — whatever the&lt;br&gt;
number turns out to be. Our expectation, stated in advance so it's falsifiable: our&lt;br&gt;
multi-hop score of 81.2% is the honest predictor of how we do there, not our 94.7%.&lt;/p&gt;

&lt;p&gt;The metrics they suggest reading instead of raw accuracy — recall@K, end-to-end task&lt;br&gt;
success, &lt;strong&gt;context inflation&lt;/strong&gt;, cross-session continuity, stability under paraphrase — are&lt;br&gt;
the right list. We already publish one of them (delivered tokens per question, item 4 below&lt;br&gt;
is what happens when you don't) and we're missing the other four. So is nearly everyone.&lt;/p&gt;
&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;We published the reference implementation: &lt;strong&gt;&lt;a href="https://github.com/todoforai/livemem" rel="noopener noreferrer"&gt;github.com/todoforai/livemem&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core is about 200 lines of TypeScript, because the architecture genuinely is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conversations ──extract──▶ dated facts + embeddings ──pack──▶ ≤ N-token block
                (1 LLM call,                          (cosine + greedy knapsack,
                 offline)                              no LLM on this path)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bet the design makes: &lt;strong&gt;extract everything once, select per question, never call an&lt;br&gt;
LLM to retrieve.&lt;/strong&gt; Selection is cosine similarity plus a greedy knapsack under a token&lt;br&gt;
budget — tens of milliseconds — so you can afford to re-select for every single question&lt;br&gt;
instead of maintaining one static "user profile" that's stale by definition.&lt;/p&gt;

&lt;p&gt;Our hosted API adds the tuned extraction prompting and the retrieval refinements described&lt;br&gt;
above — number-aware dedup, entity cards, conversation-window units — and the benchmark&lt;br&gt;
rows above were run in that configuration; the repo is the same architecture without that&lt;br&gt;
tuning. The benchmark harness in &lt;code&gt;bench/&lt;/code&gt;&lt;br&gt;
runs against either one through the same provider, so you can measure the difference&lt;br&gt;
yourself rather than taking our word for it.&lt;/p&gt;

&lt;p&gt;The per-question outputs behind our principal rows — answers, judge verdicts,&lt;br&gt;
context-token counts — are in that repo, each labelled with the answerer, judge and budget&lt;br&gt;
it used. Some intermediate numbers in this post come from validation subsets we ran during&lt;br&gt;
development and did not publish as full artifacts; they're marked as such. Numbers from&lt;br&gt;
other systems are linked to their published source and were not re-run by us. If we got&lt;br&gt;
something wrong, it's falsifiable, which is the only claim worth making about a benchmark.&lt;/p&gt;

&lt;p&gt;We know where the next points are: &lt;strong&gt;multi-hop at 81.2%&lt;/strong&gt;, aggregation, LongMemEval_S&lt;br&gt;
where we still trail Mem0 by 6.6 points, BEAM — which we haven't run and expect to hurt —&lt;br&gt;
and the hosted live path still behind our offline one. We'll publish the next number the&lt;br&gt;
same way — measured against this one, with the artifacts attached.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you want the memory without running it yourself, it's what powers agent memory in&lt;br&gt;
&lt;a href="https://todofor.ai" rel="noopener noreferrer"&gt;TODOforAI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>memory</category>
      <category>benchmarks</category>
      <category>agents</category>
      <category>llm</category>
    </item>
    <item>
      <title>voiceloop: the fastest voice agent loop in the browser is now open source</title>
      <dc:creator>Marcell Havlik</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:46:15 +0000</pubDate>
      <link>https://dev.to/todoforai/voiceloop-the-fastest-voice-agent-loop-in-the-browser-is-now-open-source-l36</link>
      <guid>https://dev.to/todoforai/voiceloop-the-fastest-voice-agent-loop-in-the-browser-is-now-open-source-l36</guid>
      <description>&lt;p&gt;&lt;em&gt;Everybody can now build the best voice agent into their own product. The #1 loop is open&lt;br&gt;
source, and the benchmark that says so is public.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo, no keys:&lt;/strong&gt; &lt;a href="https://todoforai.github.io/voiceloop/" rel="noopener noreferrer"&gt;todoforai.github.io/voiceloop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library:&lt;/strong&gt; &lt;a href="https://github.com/todoforai/voiceloop" rel="noopener noreferrer"&gt;github.com/todoforai/voiceloop&lt;/a&gt; — &lt;code&gt;npm i @todoforai/voiceloop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bench:&lt;/strong&gt; &lt;a href="https://github.com/todoforai/voice-agent-bench" rel="noopener noreferrer"&gt;github.com/todoforai/voice-agent-bench&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Why we built it
&lt;/h2&gt;

&lt;p&gt;We wanted the fluid JARVIS feel in the browser for TODOforAI — you talk, it answers within a&lt;br&gt;
second, you interrupt it mid-sentence and it just stops. We could not find a stack that did&lt;br&gt;
this properly. Closed APIs were close but not ours; the open frameworks talked over the user,&lt;br&gt;
or worse, heard their own voice through the speakers and cut themselves off.&lt;/p&gt;

&lt;p&gt;It is 2026. This should be a solved problem. So we solved it and published the whole thing:&lt;br&gt;
the loop, the numbers, and the rig that produced the numbers.&lt;/p&gt;
&lt;h2&gt;
  
  
  What voiceloop is
&lt;/h2&gt;

&lt;p&gt;A zero-dependency JavaScript library that runs the full loop in the browser:&lt;br&gt;
&lt;strong&gt;VAD → STT → LLM → TTS&lt;/strong&gt;, with the hard parts already handled.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real barge-in&lt;/strong&gt; — triggers on transcribed &lt;em&gt;novel words&lt;/em&gt;, not mic energy, so the agent's
own voice leaking into the mic never cuts it off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-echo filtering&lt;/strong&gt; — a word-match filter compares what the mic hears against what the
agent is currently saying. 0 self-interruptions in 30 echo-coupled turns with echo
cancellation fully off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First audio under a second&lt;/strong&gt; — TTS speaks sentence 1 while the LLM is still writing
sentence 2, and the LLM call starts speculatively during your end-of-turn pause.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-first&lt;/strong&gt; — Silero VAD and Piper TTS run as WASM in the tab. Free, no cloud
round-trip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialized turns&lt;/strong&gt; — rapid-fire turns, tool results, holds and replays can never talk over
each other. Locked in by 178 tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is pluggable: any OpenAI-compatible LLM, four STT providers (Web Speech,&lt;br&gt;
ElevenLabs Scribe, Deepgram Flux, Speechmatics), swappable TTS (Piper local, ElevenLabs&lt;br&gt;
cloud, or your own).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VoiceAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unlockAudio&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@todoforai/voiceloop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VoiceAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;llmUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// any OpenAI-compatible endpoint, behind your proxy&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-haiku-4-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;persona&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a friendly cooking assistant.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;unlockAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the entire integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Latency claims in voice AI are usually self-reported and unreproducible. We did not want to&lt;br&gt;
add another one, so we built &lt;a href="https://github.com/todoforai/voice-agent-bench" rel="noopener noreferrer"&gt;voice-agent-bench&lt;/a&gt;:&lt;br&gt;
a &lt;strong&gt;black-box&lt;/strong&gt; rig. A scripted "person" (byte-identical pre-generated speech) talks into a&lt;br&gt;
virtual mic, the agent's speaker output is recorded, and every score is derived from the&lt;br&gt;
audio alone. No integration needed — any agent that makes sound can be measured, including&lt;br&gt;
closed ones.&lt;/p&gt;

&lt;p&gt;Every system gets the same scripted conversations and, where the system allows it, the same&lt;br&gt;
fixed mock LLM (300ms TTFT), so the comparison isolates the voice loop from the model. 5&lt;br&gt;
conversations × 6 turns pooled, n=30, median and p95 — single runs jitter by ±300ms and are&lt;br&gt;
not worth printing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smalltalk — same mock LLM, 5×6 turns pooled
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;configuration&lt;/th&gt;
&lt;th&gt;voice→voice&lt;/th&gt;
&lt;th&gt;p95&lt;/th&gt;
&lt;th&gt;barge-in stop&lt;/th&gt;
&lt;th&gt;stalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI Realtime (speech-to-speech, own LLM) *&lt;/td&gt;
&lt;td&gt;866ms&lt;/td&gt;
&lt;td&gt;1644&lt;/td&gt;
&lt;td&gt;429ms&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;voiceloop&lt;/strong&gt; · deepgram + ElevenLabs flash&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;862ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1067&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;944ms&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;voiceloop&lt;/strong&gt; · deepgram + Piper (free, local TTS)&lt;/td&gt;
&lt;td&gt;974ms&lt;/td&gt;
&lt;td&gt;1287&lt;/td&gt;
&lt;td&gt;1463ms&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipecat 1.8.1 · deepgram + EL flash&lt;/td&gt;
&lt;td&gt;1046ms&lt;/td&gt;
&lt;td&gt;3573&lt;/td&gt;
&lt;td&gt;542ms&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElevenLabs ConvAI&lt;/td&gt;
&lt;td&gt;1454ms&lt;/td&gt;
&lt;td&gt;1632&lt;/td&gt;
&lt;td&gt;1042ms&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;voiceloop · EL Scribe + EL flash&lt;/td&gt;
&lt;td&gt;1562ms&lt;/td&gt;
&lt;td&gt;1855&lt;/td&gt;
&lt;td&gt;1566ms&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;voiceloop · Speechmatics + EL flash&lt;/td&gt;
&lt;td&gt;1706ms&lt;/td&gt;
&lt;td&gt;2069&lt;/td&gt;
&lt;td&gt;1046ms&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;voiceloop · webspeech + Piper (zero-key)&lt;/td&gt;
&lt;td&gt;2113ms&lt;/td&gt;
&lt;td&gt;2607&lt;/td&gt;
&lt;td&gt;1257ms&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Across scenarios
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;system&lt;/th&gt;
&lt;th&gt;clean&lt;/th&gt;
&lt;th&gt;hesitation&lt;/th&gt;
&lt;th&gt;talked through user&lt;/th&gt;
&lt;th&gt;echo&lt;/th&gt;
&lt;th&gt;cut itself&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI Realtime *&lt;/td&gt;
&lt;td&gt;870&lt;/td&gt;
&lt;td&gt;1290&lt;/td&gt;
&lt;td&gt;0 (yields 130ms)&lt;/td&gt;
&lt;td&gt;790&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17/30&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;voiceloop&lt;/strong&gt; · deepgram + EL flash&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;860&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1400&lt;/td&gt;
&lt;td&gt;0 (420ms)&lt;/td&gt;
&lt;td&gt;930&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;voiceloop&lt;/strong&gt; · deepgram + Piper&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1400&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipecat&lt;/td&gt;
&lt;td&gt;1050&lt;/td&gt;
&lt;td&gt;1290&lt;/td&gt;
&lt;td&gt;2 (200ms)&lt;/td&gt;
&lt;td&gt;1320&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20/30&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElevenLabs ConvAI&lt;/td&gt;
&lt;td&gt;1450&lt;/td&gt;
&lt;td&gt;1810&lt;/td&gt;
&lt;td&gt;0 (490ms)&lt;/td&gt;
&lt;td&gt;1410&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Realtime is speech-to-speech and can't use the fixed mock LLM, so its row isn't fully&lt;br&gt;
apples-to-apples.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to read it
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Clean audio:&lt;/strong&gt; voiceloop with Deepgram Flux + ElevenLabs flash is the fastest configuration&lt;br&gt;
we measured, at 862ms median — and its p95 (1067ms) is the tightest in the table by a wide&lt;br&gt;
margin. Pipecat's p95 of 3573ms on the same providers means one turn in twenty takes over&lt;br&gt;
three seconds. The free, fully local Piper path lands at 974ms with no cloud TTS at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Echo is the failure that separates the stacks.&lt;/strong&gt; Feed each system its own voice back&lt;br&gt;
through the mic (−15dB, 30ms delay, no AEC — what a laptop with the speakers on actually&lt;br&gt;
does) and the other fast stacks hear themselves as the user and cut their own replies:&lt;br&gt;
Pipecat on 20 of 30 turns, OpenAI Realtime on 17. voiceloop cut itself &lt;strong&gt;zero&lt;/strong&gt; times and&lt;br&gt;
ran echo-coupled turns at 930ms — parity with clean. Word-level echo filtering costs no&lt;br&gt;
latency once it classifies correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hesitation:&lt;/strong&gt; a user who pauses mid-sentence should not be talked over. Every stack except&lt;br&gt;
Pipecat backs off; voiceloop enters 2 of 30 hesitation turns and yields within 420ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The zero-key default is honest about its cost.&lt;/strong&gt; Browser Web Speech + Piper needs no&lt;br&gt;
account anywhere and runs the demo, but it is ~1.2s slower to close a turn than cloud STT&lt;br&gt;
(2113ms). Pick a pipeline STT provider for the numbers above.&lt;/p&gt;

&lt;p&gt;Full per-scenario tables, methodology and reproduction steps:&lt;br&gt;
&lt;a href="https://github.com/todoforai/voice-agent-bench/blob/master/results/RESULTS.md" rel="noopener noreferrer"&gt;results/RESULTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hundreds of configurations, so you don't have to
&lt;/h2&gt;

&lt;p&gt;The rows above are the survivors. Behind them are hundreds of runs across STT providers, TTS&lt;br&gt;
engines, VAD thresholds, end-of-turn debounces, barge-in minimum lengths, prefetch stability&lt;br&gt;
windows and echo-match thresholds. Every knob that mattered is exposed in&lt;br&gt;
&lt;a href="https://github.com/todoforai/voiceloop/blob/master/src/tuning.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/tuning.js&lt;/code&gt;&lt;/a&gt; with the&lt;br&gt;
default set to what won on the bench. The edge cases you would otherwise discover one&lt;br&gt;
production bug at a time — the agent interrupting itself, tool calls firing on a sentence&lt;br&gt;
the user was still amending, a hung tool stalling the next turn — are already handled and&lt;br&gt;
regression-tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, use it, beat it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try:&lt;/strong&gt; &lt;a href="https://todoforai.github.io/voiceloop/" rel="noopener noreferrer"&gt;todoforai.github.io/voiceloop&lt;/a&gt; — 20
seconds, no keys, runs in your tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use:&lt;/strong&gt; &lt;code&gt;npm i @todoforai/voiceloop&lt;/code&gt; — MIT, zero dependencies.
Point it at any OpenAI-compatible endpoint and wire in your tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beat it:&lt;/strong&gt; the bench is black-box and public. If your stack does better, add it —
&lt;a href="https://github.com/todoforai/voice-agent-bench/blob/master/ADDING_A_SUT.md" rel="noopener noreferrer"&gt;ADDING_A_SUT.md&lt;/a&gt;
is the contract. We will print the row.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the voice loop inside TODOforAI's JARVIS; the integration overhead between the&lt;br&gt;
library and the product is nil, which is exactly the point. Everybody should have the best&lt;br&gt;
voice loop. Star it, share it, contribute — let's keep the best one open source.&lt;/p&gt;

</description>
      <category>voice</category>
      <category>agents</category>
      <category>benchmarks</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
