<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://edihasaj.com/atom.xml" rel="self" type="application/atom+xml" /><link href="https://edihasaj.com/" rel="alternate" type="text/html" /><updated>2026-10-09T07:46:22+00:00</updated><id>https://edihasaj.com/atom.xml</id><title type="html">Edi Hasaj</title><subtitle>Software, AI and Thinking.</subtitle><author><name>Edi Hasaj</name></author><entry><title type="html">Playing with one-pass decision models</title><link href="https://edihasaj.com/playing-with-one-pass-decision-models/" rel="alternate" type="text/html" title="Playing with one-pass decision models" /><published>2026-10-09T07:00:00+00:00</published><updated>2026-10-09T07:00:00+00:00</updated><id>https://edihasaj.com/playing-with-one-pass-decision-models</id><content type="html" xml:base="https://edihasaj.com/playing-with-one-pass-decision-models/"><![CDATA[<p>After <a href="/jev-is-a-smart-if-statement/">writing about Jev</a>, I started playing with the same question: what happens if a model only needs to choose an answer?</p>

<p>A lot of the calls in my agent workflows have that shape. Which team should handle this request? Does this change need a closer review? The answer is a choice from a small menu. I wanted to try something different from asking a language model to write JSON and then parsing it.</p>

<p>That experiment became Surebranch. The <a href="https://huggingface.co/edihasaj/surebranch-4b">4B weights are on Hugging Face</a>.</p>

<h2 id="starting-small">Starting small</h2>

<p>The first version used a frozen sentence encoder and a small trained scorer. The idea was to encode the context once, then score the possible answers against it. I compared that with similarity scoring and existing classifiers.</p>

<p>It worked well on the banking categories the scorer had seen during training. Categories it had never seen were a different story. With all 77 banking labels competing, the trained scorer got only 27.6% of the unseen-category cases right. The simple similarity baseline got 58.2%.</p>

<p>That was an early lesson: teaching the model a set of familiar choices did not make it good at arbitrary new choices.</p>

<h2 id="trying-a-different-base">Trying a different base</h2>

<p>Later I adapted <a href="https://huggingface.co/Mapika/decider-4b">Decider 4B</a>, a Qwen3.5-based model that scores typed answer options in one forward pass. This is the basis of the published Surebranch checkpoint. It is not an encoder-only model, even though the earlier experiment was built around an encoder.</p>

<p>I tried teaching it code decisions. Given a Python program and its tests, does a particular assertion pass? Does the whole suite pass? How many tests pass?</p>

<p>For the training labels, the programs were actually executed. At inference, the model only reads the code and scores the choices. It does not run the program. I mixed general decisions into training to try to improve the code answers without losing the base model’s broader abilities.</p>

<h2 id="what-came-back">What came back</h2>

<p>The published weights were checked on 250 Python programs, each executed twice to verify the labels. Asking the questions separately gave these development results:</p>

<table>
  <thead>
    <tr>
      <th>Question</th>
      <th style="text-align: right">Accuracy</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>How many tests pass?</td>
      <td style="text-align: right">46.8%</td>
    </tr>
    <tr>
      <td>Does the whole suite pass?</td>
      <td style="text-align: right">76.8%</td>
    </tr>
    <tr>
      <td>Does one selected assertion pass?</td>
      <td style="text-align: right">84.4%</td>
    </tr>
  </tbody>
</table>

<p>Those cases were used during model selection. They are not an independent final test.</p>

<p>Asking all twelve questions together gave 82% accuracy across 2,500 individual assertions. But it detected only 203 of the 555 failing assertions. The overall accuracy looked much better than its ability to catch failures, which is the part I would care about in a review tool.</p>

<p>Question order could also change the answers. Returning a valid probability for every option does not mean those probabilities are reliable enough to act on.</p>

<h2 id="where-i-left-it">Where I left it</h2>

<p>I ended the experiment on 2 October. No candidate passed fresh production qualification, and I did not establish that this checkpoint matches Jev or meets a particular serving price.</p>

<p>The <a href="https://huggingface.co/edihasaj/surebranch-4b">model card</a> has the loading example, measured results and limitations. The weights are there for people who want to play with the approach. The training code and datasets remain private.</p>

<p>What I wanted to explore was a different way to use a model: read the context, score the available answers, and stop there. Surebranch gave me something concrete to try, including a few results that looked good until I asked a more useful question about them.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="software" /><summary type="html"><![CDATA[After writing about Jev, I tried making models choose from a fixed set of answers without generating text. Surebranch was that experiment. The weights are available, along with the parts that did not work well enough.]]></summary></entry><entry><title type="html">My AI playbook</title><link href="https://edihasaj.com/my-ai-playbook/" rel="alternate" type="text/html" title="My AI playbook" /><published>2026-09-20T12:00:00+00:00</published><updated>2026-09-20T12:00:00+00:00</updated><id>https://edihasaj.com/my-ai-playbook</id><content type="html" xml:base="https://edihasaj.com/my-ai-playbook/"><![CDATA[<p>People ask me how I use AI. They expect a prompt. It is not a prompt, it is a setup. Here it is, the short version.</p>

<h2 id="one-file-of-rules">One file of rules</h2>

<p>Every agent I run reads the same file first. Claude Code, Codex, whatever comes next. The file is public, it is called <a href="https://github.com/edihasaj/agent">AGENTS.md</a>, and it lives in one repo with the skills and the commands. Everything else links to it. I learned the hard way that if you keep two copies of a rule, one of them drifts and then breaks quietly.</p>

<p>The file is boring on purpose. Talk short. Use the repo’s package manager, dont swap it. Commit with the helper that only stages the files you name. Never delete, move to trash. Read the docs before you touch code. If CI is red, fix it until it is green. Verify end to end, and if you cant, say exactly what is missing.</p>

<p>That last one is the most important rule I have. An agent that says “should work” is the one that costs me the afternoon.</p>

<h2 id="tools-built-for-agents">Tools built for agents</h2>

<p>Most tools are built for a person with a mouse. Agents dont have a mouse. So a lot of what I built the last year is just giving them hands.</p>

<p><a href="https://github.com/edihasaj/guiport">guiport</a> lets an agent see and click a real desktop app. It reads the accesibility tree, finds the button, clicks it, types, and can save that flow as a test. Playwright but for Mac apps, basically.</p>

<p><a href="https://github.com/edihasaj/abx">abx</a> is a browser with a command line. Open a page, read it, click, fill a form, no screenshots needed most of the time.</p>

<p><a href="https://github.com/edihasaj/shotport">shotport</a> is for when pixels are actually needed. It grabs the text first, and only spends tokens on the image if you ask. Screenshots are expensive for an agent and most of the time the text is enough.</p>

<p><a href="https://github.com/edihasaj/vmlab">vmlab</a> is one command to run something on any machine. Docker box, Linux VM, Windows VM, a phone, a simulator. It spins the machine up, runs the thing, collects the evidence, and shuts it down so nothing keeps billing.</p>

<p>None of these started as products. Each one exists because an agent got stuck on something I would do in two seconds.</p>

<h2 id="the-pipeline-that-ships">The pipeline that ships</h2>

<p>When I want a change, I dont open a chat. I point <a href="https://github.com/edihasaj/shipyard">shipyard</a> at a repo and a task. A Jira key, a GitHub issue, or just a sentence.</p>

<p>It plans, makes a branch in the repos convention, does the work, runs lint and types and tests, runs a security review, then a second code review, writes the PR description, and stops. For client work it stops PR ready and I push. For my own stuff it opens the PR.</p>

<p>The agent cannot skip a gate. That is the whole design. I dont read most of the code anymore. I read the gates and I read the PR text like a colleague wrote it.</p>

<p>Sometimes I run many of these at once. A tmux grid with 32 panes, a queue of tasks, and each pane picks the next one. After a reboot a script finds the sessions and puts them back. It sounds like a lot, it is actually calmer than one chat window.</p>

<h2 id="who-checks-the-work">Who checks the work</h2>

<p>This is the part people skip. The agent finishing is not the same as the thing working.</p>

<p>Two systems check. Probeport takes a change and tries to prove it. It reads the diff, runs the native gates, then actually uses the feature through the closest real surface: the browser with abx, the desktop with guiport, another OS with vmlab. Small models do the poking, a bigger model argues with their evidence and decides if the exact revision works. The verdict comes back with screenshots and logs attached, not with “looks fine”.</p>

<p>Autoreview watches the other direction. It reads feedback, bug boards and GitHub issues, maps each one to a repo, and writes a dry run report of what it would do. Nothing happens until I enable a project and say execute. Then it can hand the work to shipyard, and the loop closes: feedback in, verified PR out.</p>

<h2 id="memory">Memory</h2>

<p>Agents forget. The first fix was a markdown file of rules, which grew until nobody read it, me included. The fix now is <a href="https://github.com/edihasaj/recall">Recall</a>. When I correct an agent in a conversation, a hook catches it, checks if it is a real rule or a one off, and stores it. Next session, on any machine, the relevant ones get injected. I said “no em dashes” once. Every agent knows it now.</p>

<h2 id="what-i-would-tell-you">What I would tell you</h2>

<p>Put the rules in one place. Give the agent tools that return text. Never let the agent decide if the gate passed. Have something else check the work before you do. Keep memory small and filtered.</p>

<p>And keep yourself in the loop where money, customers, or deleting things are involved. Everywhere else, get out of the way. The agents are better at the work than at knowing when it is done, so build for the second part.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="software" /><summary type="html"><![CDATA[No prompt tricks. One file of rules, a few tools built for agents, a pipeline that ships, and two systems that check the work before I do.]]></summary></entry><entry><title type="html">Jev is a smart if statement, and that is the point</title><link href="https://edihasaj.com/jev-is-a-smart-if-statement/" rel="alternate" type="text/html" title="Jev is a smart if statement, and that is the point" /><published>2026-09-18T18:00:00+00:00</published><updated>2026-09-18T18:00:00+00:00</updated><id>https://edihasaj.com/jev-is-a-smart-if-statement-and-that-is-the-point</id><content type="html" xml:base="https://edihasaj.com/jev-is-a-smart-if-statement/"><![CDATA[<p>Look at what a real agent pipeline actually asks a model, call by call. Is this diff risky? Which team does this ticket belong to? Is this correction worth remembering? Should this tool call be allowed? Does this search result answer the question?</p>

<p>Almost none of those need a paragraph. They need a yes, a choice, or a score. For the last year we have been asking a frontier language model, waiting a second or two, paying for the tokens, parsing JSON out of prose, and retrying when it came back malformed. It worked. It was also the wrong tool, and everyone building agents knew it.</p>

<p>On 15 September TypeSafe AI came out of stealth with <a href="https://www.typesafe.ai">Jev</a>, and their own description is the best one: a smart if statement.</p>

<h2 id="what-it-is">What it is</h2>

<p>Jev is not a language model. It cannot write text at all. You give it state, either text or JSON, and typed questions with a fixed answer space. Three kinds: yes or no, a choice from options, or a score on a scale. It returns the answer, a probability for every option, and a confidence value. That is the whole API.</p>

<p>Because the answer space is declared up front, it cannot return something outside it. No malformed JSON, no “As an AI” preamble, no hallucinated fourth option. TypeSafe calls this a System One model and trained it with a method they call RLCD, reinforcement learning for calibrated decisions, so the probabilities are supposed to match how often the answer is actually right.</p>

<p>Numbers from their launch: most calls land around 100 milliseconds, input costs $0.042 per million tokens and output is free. They claim around 200 times faster and 400 times cheaper than a frontier LLM on classification. Those are launch-week vendor claims, so treat them as such. Even if they are off by ten, the gap is still a different category.</p>

<h2 id="why-this-matters-more-than-the-model">Why this matters more than the model</h2>

<p>The thing I care about is not Jev. It is the split.</p>

<p>We have been using one kind of model for two different jobs. Generation, where you want a paragraph and some creativity is fine. And decision, where you want the same answer every time for the same input, fast, and with a number that tells you how sure it is. Frontier LLMs are great at the first and a wasteful, jittery fit for the second.</p>

<p>A decision model with typed outputs and calibrated confidence changes what you can build. You can set a threshold. Above 0.95, act. Below, escalate to a bigger model or a person. That is a normal engineering control, and until now it was hard to get from an LLM because the confidence was vibes.</p>

<p>Deterministic is the wrong word, strictly. It is still a model and it can still be wrong. But the shape of the output is deterministic, and that removes most of the failure modes I actually hit in production: parse errors, drift between runs, and a bill that scales with how chatty the model felt.</p>

<h2 id="where-i-would-use-it-tomorrow">Where I would use it tomorrow</h2>

<p>Two weeks ago I wrote about <a href="/farka-qwen-support-routing/">teaching a 9B model to route support requests</a> into billing, account, or technical. That is a choice question. It is exactly Jev’s shape.</p>

<p>In my agent pipeline there is a security review and a code review gate. The first question each one answers is “does this diff touch anything risky.” Yes or no, with a confidence I can threshold. Recall, my memory layer for agents, has a quality gate that decides whether a correction is a durable rule or a one-off. Same shape. Oktapod decides whether a tool call needs approval. Same shape.</p>

<p>Every one of those is a frontier model call today. Each one is a candidate for a 100 millisecond typed answer.</p>

<h2 id="where-it-stops">Where it stops</h2>

<p>It is three days old, and the “cannot hallucinate” line in the marketing is doing more work than it should. The output shape cannot be wrong. The answer still can. It cannot do arithmetic, compare dates, write code, or reason in more than one hop, and TypeSafe says so themselves. Text only, no images.</p>

<p>So it is not a replacement for the language model. It is the thing you put in front of it. The LLM still writes the fix, the reply, the summary. Jev decides whether to, which one, and how sure it is.</p>

<p>For the routing case I already have an alternative: a small fine-tuned model on my own hardware that got 30 of 30 on the test set. When the data cannot leave, that stays the right choice. For everything else, a lot of my if statements are going to move.</p>

<h2 id="the-bigger-point">The bigger point</h2>

<p>For a year the community built agents on the assumption that every decision costs a language model call. Whole architectures grew around minimising those calls: caching, batching, prompt compression, cheaper models for cheaper questions. A decision model makes most of that unnecessary. The decision becomes as cheap as a database lookup, and you stop designing around its cost.</p>

<p>That is the part I think people will look back on. Not the model. The moment “ask the LLM” and “ask a decision model” became two different calls.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="software" /><summary type="html"><![CDATA[Most of what my agents ask a model is not 'write this' but 'is this X or Y'. Those calls were slow, costly, and never quite deterministic. Jev is built for exactly that gap, and the category matters more than the vendor.]]></summary></entry><entry><title type="html">Why LLM writing does not feel human</title><link href="https://edihasaj.com/why-llm-writing-does-not-feel-human/" rel="alternate" type="text/html" title="Why LLM writing does not feel human" /><published>2026-09-18T00:00:00+00:00</published><updated>2026-09-18T00:00:00+00:00</updated><id>https://edihasaj.com/why-llm-writing-does-not-feel-human</id><content type="html" xml:base="https://edihasaj.com/why-llm-writing-does-not-feel-human/"><![CDATA[<p>You can tell. Someone pastes a paragraph into a chat, a pull request, a support reply, and within a sentence you know a model wrote it. Not because of a wrong fact or a bad argument. It just does not feel like a person.</p>

<p>Two years ago people blamed the words. “Delve”, “tapestry”, “leverage”. I checked that with the current models and the words are gone. I asked Claude Sonnet, Claude Opus and two Codex models to write 96 short everyday and engineering texts, then compared them with 72,000 words of commit messages written by engineers before ChatGPT. Not one “delve”. The models’ vocabulary is even plainer than the engineers’.</p>

<p>And still every answer reads like a model. So it was never the words.</p>

<h2 id="what-actually-feels-off">What actually feels off</h2>

<p>Read the human commit messages and the model answers side by side and the difference is not in any single line. It is in the shape.</p>

<p>The humans are uneven. They start mid-thought, they say “otherwise” when they are weighing a case, they leave a sentence hanging, they skip the introduction because the reader already has context. They write for one specific person on one specific day.</p>

<p>The models are even. Every answer has the same rhythm: a framing line, a body with the same punctuation, a soft close. All four models, from two different companies, share that rhythm. In my sample the em dash appears in most answers from every model and zero times in the human text. Eleven answers open with “Here’s”. The humans never did. Those are just the parts I could count. The rest of the feeling is the same thing at a level I cannot count: the same shape, every time, for everyone.</p>

<h2 id="why-i-think-this-happens">Why I think this happens</h2>

<p>A model learns what writing is from its data. For the first generation that data was mostly people. Since then a growing share of what goes into training is written by models: synthetic examples, model-rewritten documents, answers rated by other models. It is cheap, clean and endless, so labs use more of it every year.</p>

<p>Model-written data has no specific person and no specific day behind it. It is the average of every text, and the average has one shape. When a model learns from that, it learns the shape more firmly than the last one did. The words got fixed because “delve” was a public joke and easy to filter. The shape did not get fixed because nobody named it, and because the data itself keeps teaching it.</p>

<p>That is my point. The reason LLM writing does not feel human is not a style setting. It is where the writing came from.</p>

<h2 id="what-would-change-it">What would change it</h2>

<p>More real data from real contexts, and less of the synthetic kind. Companies sit on decades of text written by actual people to actual people: tickets, reviews, internal docs, support threads, engineering discussions. That is messy and specific and it belongs to someone, so it is harder to use than a generated dataset. It is also the only kind of text that carries the thing the models are missing. Quality here means provenance: written by a person, for a reason, in a place. A model trained on more of that and less of its own output would sound less like every other model.</p>

<p>I cannot see inside the labs, so this is a reading, not a proof. The small test that started it is in the <a href="https://github.com/edihasaj/edihasaj.com/tree/main/experiments/llm-vocab">experiments folder</a> of this site, if you want to check the numbers or run it against a newer model.</p>

<p>This post contains no em dashes. It took editing.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="writing" /><summary type="html"><![CDATA[The big models stopped using the words we mocked, yet you still know within a sentence that a model wrote it. My view: the data behind them is drifting away from how people actually write, and more synthetic data will make it worse.]]></summary></entry><entry><title type="html">Teaching a 9B model to route support requests</title><link href="https://edihasaj.com/farka-qwen-support-routing/" rel="alternate" type="text/html" title="Teaching a 9B model to route support requests" /><published>2026-09-13T00:00:00+00:00</published><updated>2026-09-13T00:00:00+00:00</updated><id>https://edihasaj.com/farka-qwen-support-routing</id><content type="html" xml:base="https://edihasaj.com/farka-qwen-support-routing/"><![CDATA[<p>I’m building <a href="https://farka.ai">Farka</a> to make it easier to teach a model a specific job: give it examples, train it, compare what changed, and export the result.</p>

<p>One small demo starts with a familiar problem. A support request arrives. Which team should handle it?</p>

<h2 id="give-the-model-a-clear-job">Give the model a clear job</h2>

<p>The task had three destinations: <strong>billing, account help and technical support</strong>. The model chose a team. It did not answer the customer, issue a refund or change an account.</p>

<p>We fine-tuned <a href="https://huggingface.co/Qwen/Qwen3.5-9B">Qwen3.5 9B</a> using 270 generated training examples. The run completed on 21 August 2026. This post revisits that existing result; it isn’t announcing a new training run.</p>

<h2 id="compare-what-changed">Compare what changed</h2>

<p>The saved evaluation contains before-and-after predictions for the same 30 test messages.</p>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th style="text-align: right">Requests routed correctly</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Before training</td>
      <td style="text-align: right">23 / 30</td>
    </tr>
    <tr>
      <td>After training</td>
      <td style="text-align: right">30 / 30</td>
    </tr>
  </tbody>
</table>

<p><img src="/images/posts/farka-qwen-support-routing.png" alt="Qwen3.5 9B routed 23 of 30 generated support messages correctly before training and 30 of 30 after training. No CPU comparison was run in this experiment." /></p>

<p>That is seven additional correct routes in this test. Both versions returned a valid team name for all 30 messages, so the change here is in choosing the recorded destination.</p>

<p>I checked those counts against the saved predictions. I did not rerun inference for this article.</p>

<h2 id="how-to-read-this-result">How to read this result</h2>

<p>This is a <strong>small synthetic workflow demonstration</strong>, not a customer benchmark. Thirty generated messages cannot establish production accuracy or a saving on a customer’s bill.</p>

<p>The evaluation rows also participated in checkpoint evaluation. There was no separate blind test, and I have not independently re-audited overlap between the training and evaluation message patterns. The full training protocol matters when interpreting a result this small.</p>

<p>There was also no comparison with a simple classifier in this run. Improving a language model does not establish that it is the best tool for the task. Rules, a classifier or better prompting may be enough.</p>

<h2 id="where-farka-fits">Where Farka fits</h2>

<p>The part I want Farka to make easier is that comparison loop. Start with a task you can describe and examples you have permission to use. Keep a meaningful test separate. See whether training helps before deciding to use the model.</p>

<p>For a real workflow, I would want a larger, independently held-out test, realistic mistakes and a simpler baseline before making a quality or cost claim.</p>

<p>You can explore the platform at <a href="https://farka.ai">farka.ai</a>.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="software" /><summary type="html"><![CDATA[A Qwen3.5 9B support-routing demo improved from 23 to 30 correct routes on the same 30 generated test messages. Here is the workflow, the evidence and what that result can tell us.]]></summary></entry><entry><title type="html">Paseo Changed Everything About My Multi-Account Codex Setup</title><link href="https://edihasaj.com/posts/paseo-changed-my-multi-account-codex-setup" rel="alternate" type="text/html" title="Paseo Changed Everything About My Multi-Account Codex Setup" /><published>2026-08-09T00:00:00+00:00</published><updated>2026-08-09T00:00:00+00:00</updated><id>https://edihasaj.com/posts/paseo-changed-my-multi-account-codex-setup</id><content type="html" xml:base="https://edihasaj.com/posts/paseo-changed-my-multi-account-codex-setup"><![CDATA[<p>I had just finished building a <a href="https://github.com/edihasaj/codex-account-switcher">Codex Account Switcher</a>.</p>

<p>It solved a real problem. I use two Codex accounts, one for work and one for personal projects. On macOS, running both official desktop profiles at the same time requires two isolated state directories, two Electron data directories, and a small controller to keep the Dock sane. I built the controller, documented the setup, released it, and thought the system was finished.</p>

<p>Then I found <a href="https://paseo.sh/">Paseo</a>.</p>

<p>The account switcher was still correct, but it was solving the problem one layer too high.</p>

<blockquote>
  <p>The switcher manages app windows. Paseo manages the agents themselves.</p>
</blockquote>

<p>That distinction changed my setup from two desktop applications I had to keep alive into a small, persistent agent infrastructure spread across my MacBook and Mac Studio.</p>

<h2 id="what-paseo-actually-is">What Paseo actually is</h2>

<p>Paseo is an open-source control plane for coding agents. It does not replace Codex, Claude Code, Copilot, OpenCode, or the other tools it supports. It launches the native agent CLIs already installed on your machine, using their existing credentials, configuration, skills, and tools.</p>

<p>A local daemon owns the agent processes. Desktop, mobile, web, and CLI interfaces connect to that daemon as clients.</p>

<p>That architecture sounds like an implementation detail. In practice, it is the entire point:</p>

<ul>
  <li>closing a client does not redefine where the work lives;</li>
  <li>the execution machine keeps its own code, credentials, and toolchain;</li>
  <li>another client can reconnect and continue controlling the same agents;</li>
  <li>multiple providers and multiple profiles of one provider appear in one interface;</li>
  <li>workspaces, terminals, services, schedules, and agent lifecycles share one supervisor.</li>
</ul>

<p>Paseo is closer to Docker’s daemon-and-client model than to another chat window. The UI is useful, but the daemon is the product.</p>

<h2 id="the-setup-i-ended-up-with">The setup I ended up with</h2>

<p>I now run Paseo in two places for two different jobs.</p>

<table>
  <thead>
    <tr>
      <th>Layer</th>
      <th>MacBook</th>
      <th>Mac Studio</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Role</td>
      <td>Interactive local work</td>
      <td>Always-on execution host</td>
    </tr>
    <tr>
      <td>Daemon</td>
      <td>Started by the Paseo desktop app</td>
      <td>Headless CLI daemon managed by <code class="language-plaintext highlighter-rouge">launchd</code></td>
    </tr>
    <tr>
      <td>Network</td>
      <td>Bound to loopback</td>
      <td>Bound only to its Tailscale address</td>
    </tr>
    <tr>
      <td>Remote relay</td>
      <td>Disabled</td>
      <td>Disabled</td>
    </tr>
    <tr>
      <td>Codex profiles</td>
      <td>Work and personal</td>
      <td>Work and personal</td>
    </tr>
    <tr>
      <td>Client access</td>
      <td>Desktop and CLI</td>
      <td>Desktop, CLI, or mobile over Tailscale</td>
    </tr>
  </tbody>
</table>

<p>The MacBook setup is deliberately simple. Opening Paseo starts its bundled daemon on <code class="language-plaintext highlighter-rouge">127.0.0.1:6767</code>. Local clients talk to it over loopback.</p>

<p>The Studio is the durable half. Its Paseo daemon starts at login under a user LaunchAgent with <code class="language-plaintext highlighter-rouge">RunAtLoad</code> and <code class="language-plaintext highlighter-rouge">KeepAlive</code>. The service listens on the Studio’s private Tailscale address, not <code class="language-plaintext highlighter-rouge">0.0.0.0</code>, and the public relay is disabled. A health endpoint gives me a boring test that the daemon is reachable before I blame a client.</p>

<p>The practical result is that the MacBook can be the steering wheel without being the engine. Long-running work can live on the Studio, where the repositories, compilers, authenticated CLIs, and background services already exist.</p>

<h2 id="two-codex-accounts-become-two-providers">Two Codex accounts become two providers</h2>

<p>This is the part that made my account-switching workaround feel suddenly small.</p>

<p>Paseo supports custom provider profiles. A profile can extend the built-in Codex adapter and add environment variables. I point each profile at a different <code class="language-plaintext highlighter-rouge">CODEX_HOME</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"$schema"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://paseo.sh/schemas/paseo.config.v1.json"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"agents"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"providers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"codex"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"codex-work"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="s2">"codex"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Codex Work"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"CODEX_HOME"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Users/you/.codex"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"order"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"codex-personal"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="s2">"codex"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Codex Personal"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"CODEX_HOME"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/Users/you/.codex-personal"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"order"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Paseo now shows both accounts as first-class choices. Selecting one launches the same native Codex binary with the corresponding home directory. No logout, no token copying, no wrapper flag, and no second Electron process.</p>

<p>It also preserves the boundary I care about:</p>

<table>
  <thead>
    <tr>
      <th>Shared deliberately</th>
      <th>Kept separate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">config.toml</code></td>
      <td><code class="language-plaintext highlighter-rouge">auth.json</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AGENTS.md</code></td>
      <td>sessions</td>
    </tr>
    <tr>
      <td>hooks</td>
      <td>history and logs</td>
    </tr>
    <tr>
      <td>prompts and rules</td>
      <td>local databases</td>
    </tr>
    <tr>
      <td>skills</td>
      <td>account usage and limits</td>
    </tr>
  </tbody>
</table>

<p>On both machines, the secondary Codex home links back to the primary profile’s configuration, instructions, hooks, prompts, rules, and skills. Authentication and operational state remain real files inside each account directory.</p>

<p>That gives me one operating policy with two identities. A correction to an agent rule or a new skill reaches both profiles, while credentials and session history never cross the boundary.</p>

<p>Do not symlink <code class="language-plaintext highlighter-rouge">auth.json</code> between profiles. The point is shared behavior, not shared identity.</p>

<h2 id="persistence-is-the-larger-unlock">Persistence is the larger unlock</h2>

<p>My desktop switcher kept both Codex applications open, which was enough for local multitasking. It still depended on GUI processes, a logged-in desktop session, and the health of two Electron instances.</p>

<p>Paseo moves process ownership to the daemon and its supervisor. On the Studio, <code class="language-plaintext highlighter-rouge">launchd</code> keeps the daemon available. Paseo owns the agents, records their state, and exposes the same lifecycle through every client.</p>

<p>I can start an agent from the MacBook, check it from a phone, send a follow-up through the CLI, and return to the desktop timeline later. The agent still runs where the repository and tools live: on the Studio.</p>

<p>This is a much cleaner mental model:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MacBook / phone / CLI
          |
       Tailscale
          |
   Paseo daemon on Studio
      /             \
Codex Work     Codex Personal
      \             /
 shared tools and project policy
 separate auth and session state
</code></pre></div></div>

<p>The clients are replaceable views. The daemon is the durable owner. The execution host is the source of truth.</p>

<h2 id="it-is-more-than-remote-control">It is more than remote control</h2>

<p>Remote control got my attention. Orchestration is what makes Paseo more ambitious.</p>

<p>An agent can use Paseo to discover configured providers, create an isolated worktree, launch another agent, send follow-up prompts, and keep the resulting work visible in the same application. A Codex parent can delegate to Claude, or the reverse. Paseo also exposes scheduled agents, heartbeats, supervised workspace scripts, terminals, and services.</p>

<p>Native subagents are still useful. They are fast and tightly integrated with one provider. Paseo subagents solve a different problem: lifecycle and workspace ownership across provider boundaries.</p>

<p>The worktree support matters here. Two agents editing the same checkout are still two processes racing over the same files. A control plane cannot repeal Git. Paseo-managed workspaces give parallel tasks separate branches and directories instead of asking everyone to behave carefully in one working tree.</p>

<h2 id="what-happened-to-the-account-switcher">What happened to the account switcher?</h2>

<p>I removed it from both machines.</p>

<p>The <a href="/posts/two-codex-accounts-two-dock-icons-macos">two-profile macOS setup</a> remains useful for anyone who needs the official Codex desktop experience for both accounts. It handles Electron profile isolation, focus switching, and the Dock. Paseo does not turn the official app into a multi-account app.</p>

<p>But I no longer need two official desktop processes as my control plane. Paseo gives both identities named provider profiles and keeps their agents under one supervised lifecycle. The switcher did its job, but the daemon made it unnecessary in my setup.</p>

<p>My default model is now:</p>

<ol>
  <li>Codex identities live in separate <code class="language-plaintext highlighter-rouge">CODEX_HOME</code> directories.</li>
  <li>Shared rules and skills are linked explicitly.</li>
  <li>Paseo exposes each identity as a named provider.</li>
  <li>A local daemon handles laptop work.</li>
  <li>A persistent Studio daemon handles long-running work.</li>
  <li>Tailscale provides the private path to the Studio.</li>
  <li>Desktop, mobile, and CLI clients attach to the daemon that owns the task.</li>
</ol>

<p>That model scales beyond two accounts and beyond Codex. The provider is a launch contract. The daemon is the lifecycle owner. The client is just where I happen to be looking.</p>

<h2 id="security-boundaries-i-kept">Security boundaries I kept</h2>

<p>An always-on agent daemon can execute code with the permissions of its user. Treat it like infrastructure.</p>

<p>My setup follows a few rules:</p>

<ul>
  <li>no daemon bound to every network interface;</li>
  <li>no public port forwarding;</li>
  <li>direct Studio access only across Tailscale;</li>
  <li>the hosted relay disabled because I do not need it;</li>
  <li>provider credentials left in the native provider homes;</li>
  <li>no auth files copied into Paseo configuration;</li>
  <li>a dedicated non-root user context;</li>
  <li>exact host allowlisting and a small health check;</li>
  <li>regular Paseo updates while the project is moving quickly.</li>
</ul>

<p>Paseo’s relay is end-to-end encrypted and open source, so disabling it is a topology choice, not a claim that it is unsafe. For a direct network binding, Paseo also supports password authentication. I would add that defense even on a private VPN when the daemon is shared by several devices.</p>

<h2 id="the-honest-limitations">The honest limitations</h2>

<p>Paseo does not merge accounts, chats, billing, or usage limits. It gives each profile a clean launch path.</p>

<p>It does not make unsafe agent permissions safe. If a Codex profile has broad filesystem and network access, launching it through Paseo preserves that power.</p>

<p>It does not make one checkout safe for parallel edits. Use worktrees.</p>

<p>It also does not remove the need to understand where state lives. <code class="language-plaintext highlighter-rouge">PASEO_HOME</code> owns Paseo state. Each provider still owns its own credentials and sessions. Repositories still live on the execution host.</p>

<p>Finally, Paseo is young and changing quickly. I am comfortable running it because the architecture is local-first, the code is public, the underlying CLIs remain usable without it, and my account isolation does not depend on a proprietary cloud database.</p>

<h2 id="the-real-change">The real change</h2>

<p>I started with a UI problem: how do I keep two Codex accounts open on one Mac?</p>

<p>The better question was: who should own the lifecycle of my agents?</p>

<p>Once the answer became “a daemon on the machine doing the work,” the rest simplified. Multiple accounts became provider profiles. The Studio became an execution host. The MacBook and phone became clients. Persistence stopped depending on a window staying open. Orchestration became possible without replacing the tools I already use.</p>

<p>The account switcher fixed my Dock. Paseo made me stop needing the switcher.</p>

<p>Paseo changed the system.</p>

<h2 id="frequently-asked-questions">Frequently asked questions</h2>

<h3 id="can-paseo-run-multiple-codex-accounts">Can Paseo run multiple Codex accounts?</h3>

<p>Yes. Define multiple custom providers that extend Codex and give each one a different <code class="language-plaintext highlighter-rouge">CODEX_HOME</code>. Each profile uses its own Codex authentication and state.</p>

<h3 id="does-paseo-replace-codex">Does Paseo replace Codex?</h3>

<p>No. It launches and supervises the native Codex CLI. Your installed binary, subscription, configuration, skills, MCP servers, and credentials remain yours.</p>

<h3 id="will-work-continue-if-i-close-the-desktop-app">Will work continue if I close the desktop app?</h3>

<p>The daemon owns the agent lifecycle, not the desktop window. Keep the daemon and execution host running. Another Paseo client can reconnect to it later.</p>

<h3 id="why-use-a-mac-studio-daemon-instead-of-ssh-alone">Why use a Mac Studio daemon instead of SSH alone?</h3>

<p>SSH gives me a shell. Paseo adds durable agent lifecycle, reconnectable timelines, named providers, mobile and desktop clients, orchestration, workspaces, schedules, and supervised services.</p>

<h3 id="is-the-paseo-relay-required">Is the Paseo relay required?</h3>

<p>No. Paseo supports direct connections. I use a Tailscale-only address and disable the relay. The official relay is an alternative for simpler remote access and provides end-to-end encryption.</p>

<h3 id="do-i-still-need-the-codex-account-switcher">Do I still need the Codex Account Switcher?</h3>

<p>Not for my current workflow. I removed it from both machines. It is still useful for simultaneous use of two official Codex desktop profiles because Paseo handles native CLI agents and their lifecycle, not Electron profile isolation inside OpenAI’s app.</p>

<p>More: <a href="/topics/ai-agents/">AI agent essays</a> and <a href="/topics/codex/">OpenAI Codex guides</a>.</p>

<hr />

<p>Sources:</p>

<ul>
  <li><a href="https://paseo.sh/docs">Paseo documentation</a></li>
  <li><a href="https://paseo.sh/docs/providers">How Paseo providers work</a></li>
  <li><a href="https://paseo.sh/docs/custom-providers">Paseo custom provider profiles</a></li>
  <li><a href="https://paseo.sh/docs/orchestration">Paseo orchestration</a></li>
  <li><a href="https://paseo.sh/docs/worktrees">Paseo worktrees</a></li>
  <li><a href="https://paseo.sh/docs/security">Paseo security model</a></li>
  <li><a href="https://github.com/getpaseo/paseo">Paseo on GitHub</a></li>
</ul>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="agents" /><category term="Codex" /><category term="macOS" /><category term="tooling" /><category term="open-source" /><summary type="html"><![CDATA[I built a macOS account switcher to keep two Codex profiles alive. Paseo moved the solution down a layer: persistent daemons, named account profiles, remote control, and agent orchestration across my MacBook and Mac Studio.]]></summary></entry><entry><title type="html">How to Run Two Codex Accounts on macOS with Separate Profiles</title><link href="https://edihasaj.com/posts/two-codex-accounts-two-dock-icons-macos" rel="alternate" type="text/html" title="How to Run Two Codex Accounts on macOS with Separate Profiles" /><published>2026-07-26T00:00:00+00:00</published><updated>2026-08-01T00:00:00+00:00</updated><id>https://edihasaj.com/posts/two-codex-accounts-two-dock-icons-macos</id><content type="html" xml:base="https://edihasaj.com/posts/two-codex-accounts-two-dock-icons-macos"><![CDATA[<blockquote>
  <p><strong>Update, August 1, 2026:</strong> The original version used two pinned wrapper apps.
That looked tidy until macOS created extra running-app tiles and Spotlight found
old launcher copies. The improved setup keeps one official Dock icon and uses
a menu-bar controller to open or focus either isolated account.</p>
</blockquote>

<p>The result is practical multitasking without modifying the signed Codex app:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Option-Command-1</code> opens or focuses Primary;</li>
  <li><code class="language-plaintext highlighter-rouge">Option-Command-2</code> opens or focuses Secondary;</li>
  <li>switching focus does not quit the other process;</li>
  <li><strong>Open Both</strong> starts both profiles;</li>
  <li><strong>Quit Primary</strong> and <strong>Quit Secondary</strong> stop them explicitly;</li>
  <li>Applications and Spotlight contain only the current controller and launchers.</li>
</ul>

<h2 id="how-the-two-profiles-are-isolated">How the two profiles are isolated</h2>

<table>
  <thead>
    <tr>
      <th>Profile state</th>
      <th>Primary</th>
      <th>Secondary</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Codex configuration and authentication</td>
      <td><code class="language-plaintext highlighter-rouge">~/.codex</code></td>
      <td><code class="language-plaintext highlighter-rouge">~/.codex-work</code></td>
    </tr>
    <tr>
      <td>Desktop application data</td>
      <td>normal app data</td>
      <td><code class="language-plaintext highlighter-rouge">~/Library/Application Support/Codex Second</code></td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">CODEX_HOME</code> isolates Codex configuration and authentication. The separate
<code class="language-plaintext highlighter-rouge">--user-data-dir</code> is also required because the desktop app uses Electron and
otherwise hands a second launch back to the first process.</p>

<h2 id="test-the-secondary-account">Test the secondary account</h2>

<p>Run this in Terminal before installing the switcher:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">codex_app</span><span class="o">=</span><span class="s2">"/Applications/ChatGPT.app"</span>
<span class="o">[[</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$codex_app</span><span class="s2">"</span> <span class="o">]]</span> <span class="o">||</span> <span class="nv">codex_app</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Applications/ChatGPT.app"</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.codex-work"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Library/Application Support/Codex Second"</span>

open <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$codex_app</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">--env</span> <span class="s2">"CODEX_HOME=</span><span class="nv">$HOME</span><span class="s2">/.codex-work"</span> <span class="se">\</span>
  <span class="nt">--args</span> <span class="s2">"--user-data-dir=</span><span class="nv">$HOME</span><span class="s2">/Library/Application Support/Codex Second"</span>
</code></pre></div></div>

<p>A second Codex window should open. Sign into the other ChatGPT account once;
later launches reuse that profile’s isolated authentication and desktop state.</p>

<h2 id="install-the-account-switcher">Install the account switcher</h2>

<p>Download and run the installer:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> <span class="se">\</span>
  https://edihasaj.com/assets/scripts/install-codex-second.zsh <span class="se">\</span>
  <span class="nt">-o</span> <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Downloads/install-codex-second.zsh"</span>

zsh <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Downloads/install-codex-second.zsh"</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">swiftc</code> is unavailable, install Apple’s Command Line Tools first:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select <span class="nt">--install</span>
</code></pre></div></div>

<p>The installer:</p>

<ul>
  <li>leaves <code class="language-plaintext highlighter-rouge">/Applications/ChatGPT.app</code> unchanged and preserves its signature;</li>
  <li>installs <code class="language-plaintext highlighter-rouge">~/Applications/Codex Account Switcher.app</code> as a login item;</li>
  <li>installs background launchers named <code class="language-plaintext highlighter-rouge">Codex Primary</code>, <code class="language-plaintext highlighter-rouge">Codex Secondary</code>, and
<code class="language-plaintext highlighter-rouge">Codex Open Both</code> in <code class="language-plaintext highlighter-rouge">~/Applications</code>;</li>
  <li>keeps exactly one official ChatGPT/Codex Dock pin;</li>
  <li>removes stale launcher pins that cause duplicate running-app tiles;</li>
  <li>moves replaced launchers to the Trash so they remain recoverable.</li>
</ul>

<p>The three Applications launchers signal the menu controller and immediately
exit. Because they are background agents, they do not create their own Dock
tiles. They are useful from Finder, Launchpad, Raycast, or Spotlight.</p>

<h2 id="multitask-without-stopping-background-work">Multitask without stopping background work</h2>

<p>After installation, choose <strong>Open Both</strong> or open the two profile launchers.
Both desktop processes remain alive. <code class="language-plaintext highlighter-rouge">Option-Command-1</code> and
<code class="language-plaintext highlighter-rouge">Option-Command-2</code> only change focus; they do not terminate either account.</p>

<p>That means a local task can continue in the background while you work in the
other account. Keep the Mac awake and leave both processes open. A Codex crash,
manual quit, restart, or system sleep can still interrupt local work.</p>

<p>When you want to reduce memory use or avoid running two instances, use
<strong>Quit Primary</strong> or <strong>Quit Secondary</strong> from the menu-bar controller.</p>

<h2 id="keep-the-dock-clean">Keep the Dock clean</h2>

<p>Pin only the installed OpenAI app. Do not pin <code class="language-plaintext highlighter-rouge">Codex Primary</code>,
<code class="language-plaintext highlighter-rouge">Codex Secondary</code>, or <code class="language-plaintext highlighter-rouge">Codex Open Both</code>.</p>

<p>Both Codex processes share the same signed bundle identity. Depending on the
macOS version and current Dock state, macOS may group them under the official
tile or temporarily show another running tile. Pinning wrapper apps cannot
reliably force two permanent identities and is what causes the confusing
duplicate-icon behavior.</p>

<h2 id="verify-both-accounts">Verify both accounts</h2>

<p>Choose <strong>Open Both</strong>, then run:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pgrep <span class="nt">-alf</span> <span class="s2">"ChatGPT.app/Contents/MacOS/ChatGPT"</span>
</code></pre></div></div>

<p>You should see two main processes. The secondary process includes:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--user-data-dir=/Users/you/Library/Application Support/Codex Second
</code></pre></div></div>

<p>Each window should remember its own account after quitting and reopening. Do
not place either profile directory in a repository or public sync folder. This
setup isolates local profiles; it does not merge chats, projects, billing, or
usage limits.</p>

<h2 id="frequently-asked-questions">Frequently asked questions</h2>

<h3 id="can-i-run-two-codex-accounts-at-the-same-time-on-one-mac">Can I run two Codex accounts at the same time on one Mac?</h3>

<p>Yes. The signed app runs twice, while the second process uses an isolated
<code class="language-plaintext highlighter-rouge">CODEX_HOME</code> and Electron user-data directory.</p>

<h3 id="why-is-codex_home-alone-not-enough">Why is <code class="language-plaintext highlighter-rouge">CODEX_HOME</code> alone not enough?</h3>

<p>It separates Codex configuration and authentication, but not all Electron
desktop state. The separate user-data directory allows the second desktop
process to start independently.</p>

<h3 id="can-both-accounts-keep-working-while-i-switch-windows">Can both accounts keep working while I switch windows?</h3>

<p>Yes. The switcher opens or focuses the requested profile and leaves the other
one running. Use the explicit Quit menu items when you want to stop one.</p>

<h3 id="why-not-pin-two-custom-codex-launchers">Why not pin two custom Codex launchers?</h3>

<p>The launchers and real running app have different identities, while both real
Codex processes share OpenAI’s bundle identity. macOS therefore cannot map two
custom permanent pins cleanly to the two processes.</p>

<h3 id="does-this-modify-the-installed-codex-app">Does this modify the installed Codex app?</h3>

<p>No. The official bundle is neither edited nor re-signed. The installer only
builds and signs the small local controller and its background launchers.</p>

<h3 id="is-this-guaranteed-to-be-crash-free">Is this guaranteed to be crash-free?</h3>

<p>No. Separate profile directories prevent state collisions, but simultaneous
desktop instances remain a local workaround. Use one process when maximum
stability matters.</p>

<p>More: <a href="/topics/codex/">OpenAI Codex guides</a>.</p>

<hr />

<p>Sources:</p>

<ul>
  <li><a href="/assets/scripts/install-codex-second.zsh">Codex account-switcher installer</a></li>
  <li><a href="https://learn.chatgpt.com/docs/config-file/environment-variables">Codex environment variables</a></li>
  <li><a href="https://learn.chatgpt.com/docs/config-file/config-advanced#config-and-state-locations">Codex configuration and state locations</a></li>
  <li><a href="https://learn.chatgpt.com/docs/auth">Codex authentication</a></li>
</ul>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="Codex" /><category term="macOS" /><category term="tooling" /><category term="productivity" /><summary type="html"><![CDATA[Run two Codex accounts on one Mac with isolated profiles, one clean Dock icon, background multitasking, and fast account switching.]]></summary></entry><entry><title type="html">Anthropic Pulled Fable. Build Your Own Intelligence.</title><link href="https://edihasaj.com/posts/anthropic-pulled-fable-build-your-own-intelligence" rel="alternate" type="text/html" title="Anthropic Pulled Fable. Build Your Own Intelligence." /><published>2026-06-13T00:00:00+00:00</published><updated>2026-06-13T00:00:00+00:00</updated><id>https://edihasaj.com/posts/anthropic-pulled-fable-build-your-own-intelligence</id><content type="html" xml:base="https://edihasaj.com/posts/anthropic-pulled-fable-build-your-own-intelligence"><![CDATA[<p>Anthropic launched Claude Fable 5 on June 9. Three days later, on June 12, they turned it off. Not for one region, not for one customer tier, for everyone on the planet at once. Fable 5 and Mythos 5, gone, the same week they shipped.</p>

<p>This was not a safety rollback or a capacity problem. The US government issued an export-control directive under national security authorities barring any foreign national from accessing the models, whether they sit inside or outside the United States, including Anthropic’s own foreign-national employees. There is no clean way to comply with an order like that while keeping a global product live, so Anthropic did the only thing the order left them: they pulled the plug for everybody. Every other Anthropic model stayed up. Only the two most capable ones went dark.</p>

<p>The cited reason is the interesting part. According to Anthropic, the directive leaned on the fact that Fable 5 was unusually good at finding software vulnerabilities. That is the whole story in one line. A model crossed a capability threshold where a government decided it was a weapon, and weapons do not get exported.</p>

<h2 id="the-thing-nobody-priced-in">the thing nobody priced in</h2>

<p>Most of the AI conversation for the last two years has been about capability. Can it code, can it reason, can it use a computer, can it replace a junior. We argued about benchmarks and pricing and context windows. We did not spend nearly enough time on a much simpler question. Who can turn it off, and when.</p>

<p>Now we have the answer, with a date on it. June 12, 2026. Frontier intelligence is no longer just a product you rent. It is a strategic asset that a single government can revoke overnight, retroactively, after you have already built on top of it. Your roadmap, your agents, your internal tooling, all of it sitting on an API that a directive can switch off before lunch.</p>

<p>If your company is outside the US, this is not hypothetical anymore. The most capable American models are now legally off limits to you, not because of price or availability, but because of your passport. That is a hard constraint, and it does not negotiate.</p>

<h2 id="this-is-the-start-not-the-exception">this is the start, not the exception</h2>

<p>It is tempting to read this as a one-off. A single tense week, a single model, a policy that gets walked back once the lawyers calm down. I do not think that is what is happening.</p>

<p>The pattern underneath is durable. Models keep getting better at exactly the things states care about most, which is finding vulnerabilities, writing exploits, moving through systems, and reasoning about targets at scale. The better they get at those things, the more they look like dual-use technology, and dual-use technology gets export controls the same way chips and crypto and centrifuges did. Fable 5 is the first frontier model to hit that wall in public. It will not be the last. Expect more directives, more sudden suspensions, more capability tiers that quietly become classified by function.</p>

<p>So the intelligence war stops being a metaphor about labs competing on benchmarks. It becomes a literal one between countries, fought over who can build, run, and gate the smartest systems, and who is left renting access that can be cancelled.</p>

<h2 id="the-lesson-is-sovereignty">the lesson is sovereignty</h2>

<p>Here is where it lands for me. If intelligence is now a national asset that can be switched off, then every serious country, and frankly every serious company, has to treat the ability to produce its own intelligence as infrastructure. Not a nice-to-have, not a research vanity project, infrastructure on the level of power and water.</p>

<p>This is not a fringe take anymore. More than 60 nations have published formal AI strategies, and over 30 have put real money behind building domestic capability. Jensen Huang said it plainly at Davos in January: every country should build its own AI, on its own infrastructure, trained on its own language and culture, with its national intelligence as part of its own ecosystem. France has Mistral. India is shipping models tuned for Hindi, Tamil, and the rest. Saudi Arabia is pushing HUMAIN. Brazil committed billions to a sovereign plan with its own supercomputer. There is a 720 million dollar build-out putting AI factories across Africa for data sovereignty. The world already started moving, and the Fable suspension just proved why.</p>

<p>Sovereign AI is the unglamorous version of independence. It means your own compute, your own data, your own talent, your own governance, and crucially your own weights that no foreign directive can revoke. The country or company that owns the stack gets to keep thinking when the export controls come down. The one that rents it gets a 503 and a press release.</p>

<h2 id="what-i-would-actually-do">what i would actually do</h2>

<p>I am not saying every startup needs to train a frontier model. That is a fantasy and a good way to burn your runway. But the strategic posture has changed, and the cheap moves are obvious now.</p>

<p>Stop building your core on a single provider you cannot control. Keep capable open-weight models in your stack, the ones you can run yourself on hardware you can point at, so that when an API goes dark you degrade instead of dying. Treat model access the same way you treat any other dependency that a government could sanction, which means assume it can disappear and design so that it disappearing is survivable. And if you are operating at the level of a country, the calculus is even simpler. Own the compute, fund the labs, keep the weights at home, because the alternative is letting someone else decide when your economy gets to be smart.</p>

<p>Fable 5 was a great model. For three days. The model is not the point. The point is that the smartest tools on earth are now things states will take away from you the moment they decide you should not have them, and the only durable answer to that is to build your own. 2026 is the year that stopped being a slogan and became a survival requirement.</p>

<p>The patch, as always, is our job. So is the intelligence.</p>

<hr />

<p>Sources:</p>

<ul>
  <li><a href="https://www.anthropic.com/news/fable-mythos-access">Statement on the US government directive to suspend access to Fable 5 and Mythos 5 (Anthropic)</a></li>
  <li><a href="https://www.cnbc.com/2026/06/12/anthropic-disables-access-to-fable-5-and-mythos-5-to-comply-with-government-directive.html">Anthropic disables access to Fable 5 and Mythos 5 to comply with government directive (CNBC)</a></li>
  <li><a href="https://time.com/article/2026/06/13/anthropic-fable-mythos-ban-US-security/">Anthropic Pulls Its Most Powerful AI Models After U.S. Bars Foreign Access (TIME)</a></li>
  <li><a href="https://www.aljazeera.com/news/2026/6/13/us-orders-anthropic-to-disable-ai-models-for-all-foreign-nationals">US orders Anthropic to disable AI models for all foreign nationals (Al Jazeera)</a></li>
  <li><a href="https://gulfnews.com/technology/wgs-2026-ai-is-becoming-a-sovereignty-decision-for-governments-global-leaders-warn-1.500431704">Jensen Huang on sovereign AI, WGS / Davos 2026 (Gulf News)</a></li>
  <li><a href="https://callsphere.ai/blog/rise-of-sovereign-ai-nations-building-independent-capabilities">The Rise of Sovereign AI: How Nations Are Building Independent AI Capabilities (CallSphere)</a></li>
</ul>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="sovereignty" /><category term="geopolitics" /><category term="Anthropic" /><category term="national-security" /><category term="open-source" /><summary type="html"><![CDATA[Anthropic launched Fable 5 on June 9 and killed it for the entire planet on June 12, on a US government order. The model itself was fine. The lesson is that frontier intelligence is now a national asset that can be switched off, and if you do not own yours, someone else decides when you get to think.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://edihasaj.com/images/posts/anthropic-pulled-fable-2026-06-13.jpg" /><media:content medium="image" url="https://edihasaj.com/images/posts/anthropic-pulled-fable-2026-06-13.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Universal Memory Protocol: Simple Agent Memory That Moves</title><link href="https://edihasaj.com/posts/universal-memory-protocol-simple-agent-memory" rel="alternate" type="text/html" title="Universal Memory Protocol: Simple Agent Memory That Moves" /><published>2026-06-07T00:00:00+00:00</published><updated>2026-06-07T00:00:00+00:00</updated><id>https://edihasaj.com/posts/universal-memory-protocol-simple-agent-memory</id><content type="html" xml:base="https://edihasaj.com/posts/universal-memory-protocol-simple-agent-memory"><![CDATA[<p>I built <a href="https://github.com/edihasaj/recall">Recall</a> because I was tired of correcting coding agents about the same repo rules every week. That solved a local problem. The agent would remember “use pnpm here”, “run this gate before handoff”, “do not touch this folder”, and the next session would start with that context already loaded.</p>

<p>Then I hit the next problem.</p>

<p>The memory was useful, but it was trapped in one shape. Claude Code had one way to hold project rules. Codex had another. ChatGPT had another. Local agents had files. Some frameworks had vector stores. Some had graph stores. Some had nothing. Every tool was learning the same things about me and my projects, but none of them could share those memories in a clean way.</p>

<p>That is why I started <a href="https://github.com/edihasaj/universal-memory-protocol">Universal Memory Protocol</a>, or UMP.</p>

<p>The simple version:</p>

<blockquote>
  <p>MCP lets agents use tools. A2A lets agents talk to agents. UMP lets agents carry memory.</p>
</blockquote>

<p>It is not meant to be a new database. It is not meant to replace Recall, Mem0, Letta, Zep, Obsidian, Postgres, SQLite, Redis, or a vector index. It is the small contract between them.</p>

<figure class="post-figure">
<svg viewBox="0 0 760 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Universal Memory Protocol structure" style="width:100%;height:auto;color:currentColor">
  <style>
    .ttl { font: 700 16px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .sub { font: 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor; opacity: 0.74 }
    .lbl { font: 700 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .txt { font: 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor; opacity: 0.78 }
    .box { fill: none; stroke: currentColor; stroke-width: 1.2; opacity: 0.58 }
    .a { fill: #14b8a6; opacity: 0.16 }
    .b { fill: #f59e0b; opacity: 0.18 }
    .c { fill: #6366f1; opacity: 0.15 }
    .d { fill: #ef4444; opacity: 0.12 }
    .line { stroke: currentColor; stroke-width: 1.4; opacity: 0.42; fill: none }
  </style>

  <text x="28" y="34" class="ttl">UMP is three boring pieces</text>
  <text x="28" y="54" class="sub">one record shape, one tiny operation set, three ways to connect</text>

  <rect x="40" y="82" width="200" height="210" rx="8" class="a" />
  <rect x="40" y="82" width="200" height="210" rx="8" class="box" />
  <text x="66" y="116" class="lbl">1. Portable record</text>
  <text x="66" y="146" class="txt">kind</text>
  <text x="66" y="166" class="txt">body</text>
  <text x="66" y="186" class="txt">scope</text>
  <text x="66" y="206" class="txt">time</text>
  <text x="66" y="226" class="txt">provenance</text>
  <text x="66" y="246" class="txt">consent</text>
  <text x="66" y="266" class="txt">integrity</text>

  <rect x="280" y="82" width="200" height="210" rx="8" class="b" />
  <rect x="280" y="82" width="200" height="210" rx="8" class="box" />
  <text x="306" y="116" class="lbl">2. Six operations</text>
  <text x="306" y="146" class="txt">capabilities</text>
  <text x="306" y="166" class="txt">recall</text>
  <text x="306" y="186" class="txt">remember</text>
  <text x="306" y="206" class="txt">get</text>
  <text x="306" y="226" class="txt">revise</text>
  <text x="306" y="246" class="txt">forget</text>
  <text x="306" y="266" class="txt">feedback at L3</text>

  <rect x="520" y="82" width="200" height="210" rx="8" class="c" />
  <rect x="520" y="82" width="200" height="210" rx="8" class="box" />
  <text x="546" y="116" class="lbl">3. Bindings</text>
  <text x="546" y="146" class="txt">MCP tools</text>
  <text x="546" y="166" class="txt">HTTP endpoints</text>
  <text x="546" y="186" class="txt">JSON files</text>
  <text x="546" y="206" class="txt">Markdown files</text>
  <text x="546" y="226" class="txt">.well-known discovery</text>

  <path d="M240 187 H280" class="line" />
  <path d="M480 187 H520" class="line" />

  <rect x="120" y="312" width="520" height="30" rx="6" class="d" />
  <text x="380" y="332" text-anchor="middle" class="txt">The storage engine still competes on retrieval, ranking, decay, and compression.</text>
</svg>
</figure>

<h2 id="the-shape">The Shape</h2>

<p>UMP is deliberately small:</p>

<ol>
  <li>A portable memory record.</li>
  <li>Six core operations.</li>
  <li>MCP, HTTP, and file bindings.</li>
  <li>Conformance levels from simple export to full signed runtime.</li>
</ol>

<p>The record is the main thing. A memory needs a type, a body, a scope, time fields, lifecycle hints, relations, provenance, consent, and integrity.</p>

<p>In normal words:</p>

<blockquote>
  <p>what is this memory, who owns it, where is it valid, when was it true, where did it come from, who may see it, and can I verify it?</p>
</blockquote>

<p>That sounds obvious, but most agent memory today does not carry all of that. It might have text and an embedding. It might have a user id. It might have a timestamp. But the moment you try to move it from one agent or store to another, the missing fields matter.</p>

<p>If a memory says “use pnpm”, is that global, repo-specific, team-specific, or only true for one branch? If it says “the deployment target is staging”, was that true yesterday or is it true now? If an agent wrote it, did the user approve it? If it contains a secret or personal detail, should it be exported at all?</p>

<p>UMP puts those questions into the record instead of leaving them as product-specific behavior.</p>

<h2 id="how-i-came-to-it">How I Came To It</h2>

<p>The first version of the idea came from pain, not architecture.</p>

<p>I use multiple agents. I switch between tools. I also work across enough repos that the same mistake keeps coming back in different clothes. One agent learns a rule, another agent does not know it, and I become the bridge.</p>

<p>Recall proved the useful part: corrections can become repo memory, memory can be ranked, stale memories can be retired, and the agent gets better without a giant instruction file.</p>

<p>But Recall also made the protocol gap obvious. The useful abstraction was not “Recall”. It was:</p>

<blockquote>
  <p>an agent needs to ask for relevant memory, write new memory, revise old memory, forget unsafe memory, and prove where memory came from.</p>
</blockquote>

<p>That maps cleanly to operations:</p>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">capabilities</code></td>
      <td>What does this memory server support?</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">recall</code></td>
      <td>Find relevant memories for this scope and query.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">remember</code></td>
      <td>Store a new memory.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">get</code></td>
      <td>Fetch a memory by id.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">revise</code></td>
      <td>Replace a memory without destroying its history.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">forget</code></td>
      <td>Tombstone or remove a memory with a reason.</td>
    </tr>
  </tbody>
</table>

<p>That is enough for a small client. It is also enough for adapters. A fancy graph memory system and a simple JSON file can speak the same verbs, while still behaving differently underneath.</p>

<h2 id="what-it-fixes">What It Fixes</h2>

<p>UMP fixes four practical problems.</p>

<p>First, memory lock-in. If Claude learns a project rule and Codex cannot read it, that is not memory. That is product state. UMP makes the memory record portable so it can move between hosts and stores.</p>

<p>Second, stale memory. A lot of memory systems overwrite facts. That is dangerous because old context disappears and the agent cannot reason about when something was true. UMP uses bi-temporal records and supersession. A memory can be replaced without pretending the old one never existed.</p>

<p>Third, trust. Recalled memory is not sacred. It can be wrong, stale, malicious, or out of scope. UMP treats memory as untrusted input and requires safe rehydration before it enters the model context. The context block says, in effect, “these are references, not instructions.”</p>

<p>Fourth, ownership. A useful memory record should carry provenance, consent, and eventually signatures. UMP uses existing standards where they fit, like <a href="https://www.w3.org/TR/prov-o/">W3C PROV</a> for provenance, <a href="https://www.w3.org/TR/did-core/">DID</a> for owner identity, and <a href="https://www.rfc-editor.org/rfc/rfc8785.html">RFC 8785 JSON canonicalization</a> for deterministic signing.</p>

<p>The important point is what UMP does not try to fix. It does not decide which embedding model is best. It does not mandate graph search. It does not freeze a decay curve. It does not tell Recall, Zep, Letta, Mem0, SQLite, or Postgres how to rank memory.</p>

<p>It standardizes the parts that must match for memory to travel. The intelligence stays inside the engine.</p>

<h2 id="how-it-runs">How It Runs</h2>

<p>The fastest way to use it is through <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>, because most agent hosts already know how to talk to MCP servers.</p>

<div class="language-jsonc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ump"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npx"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"-y"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@universalmemoryprotocol/core"</span><span class="p">,</span><span class="w"> </span><span class="s2">"ump-memory"</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>That gives the host <code class="language-plaintext highlighter-rouge">ump.recall</code>, <code class="language-plaintext highlighter-rouge">ump.remember</code>, <code class="language-plaintext highlighter-rouge">ump.get</code>, <code class="language-plaintext highlighter-rouge">ump.revise</code>, <code class="language-plaintext highlighter-rouge">ump.forget</code>, and <code class="language-plaintext highlighter-rouge">ump.capabilities</code>.</p>

<p>By default, the reference server writes a portable file at:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/.ump/memory.ump.json
</code></pre></div></div>

<p>Point another MCP host at the same store and it can use the same memories. That is the money shot: write in one agent, recall in another.</p>

<p>There is also an HTTP binding for apps that do not speak MCP, and a file binding for plain exports. That matters because adoption should not require the full runtime. A project can start at L0 with a <code class="language-plaintext highlighter-rouge">*.ump.json</code> or <code class="language-plaintext highlighter-rouge">*.ump.md</code> file, then move to an L1 or L2 server later.</p>

<figure class="post-figure">
<svg viewBox="0 0 760 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="UMP memory flow from one agent to another" style="width:100%;height:auto;color:currentColor">
  <style>
    .ttl { font: 700 15px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .lbl { font: 700 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .txt { font: 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor; opacity: 0.78 }
    .box { fill: none; stroke: currentColor; stroke-width: 1.2; opacity: 0.58 }
    .agent { fill: #14b8a6; opacity: 0.16 }
    .store { fill: #f59e0b; opacity: 0.18 }
    .agent2 { fill: #6366f1; opacity: 0.15 }
    .line { stroke: currentColor; stroke-width: 1.5; opacity: 0.44; fill: none; marker-end: url(#arrow) }
  </style>
  <defs>
    <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="currentColor" opacity="0.55" />
    </marker>
  </defs>

  <text x="28" y="34" class="ttl">One memory, two agents</text>

  <rect x="46" y="86" width="180" height="92" rx="8" class="agent" />
  <rect x="46" y="86" width="180" height="92" rx="8" class="box" />
  <text x="76" y="122" class="lbl">Agent A</text>
  <text x="76" y="146" class="txt">calls ump.remember</text>

  <rect x="300" y="66" width="160" height="132" rx="8" class="store" />
  <rect x="300" y="66" width="160" height="132" rx="8" class="box" />
  <text x="338" y="112" class="lbl">UMP store</text>
  <text x="330" y="138" class="txt">signed record</text>
  <text x="330" y="158" class="txt">scope checked</text>
  <text x="330" y="178" class="txt">history kept</text>

  <rect x="534" y="86" width="180" height="92" rx="8" class="agent2" />
  <rect x="534" y="86" width="180" height="92" rx="8" class="box" />
  <text x="564" y="122" class="lbl">Agent B</text>
  <text x="564" y="146" class="txt">calls ump.recall</text>

  <path d="M226 132 H300" class="line" />
  <path d="M460 132 H534" class="line" />

  <text x="380" y="242" text-anchor="middle" class="txt">The host changes. The memory record does not.</text>
</svg>
</figure>

<h2 id="how-fast-it-is">How Fast It Is</h2>

<p>For the default local file store, the answer is boring and good: around 5ms recall at 3,000 records in the current benchmarks. That is the right default for most project memory. It is local, portable, and fast enough that the agent does not feel like it is waiting on a separate brain.</p>

<p>The richer Recall-backed path costs more because it does actual semantic retrieval. In my current benchmark notes, it is roughly 100ms to write and roughly 200ms to recall after warming the local embedding model. The tradeoff is quality: paraphrase top-1 recall improves from 1 out of 8 with lexical matching to 5 out of 8 with Recall’s vector plus BM25 search.</p>

<p>So the rule is simple:</p>

<table>
  <thead>
    <tr>
      <th>Store</th>
      <th>Best for</th>
      <th>Rough speed</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>JSON file</td>
      <td>portable local memory, repo rules, simple preferences</td>
      <td>about 5ms recall at 3k records</td>
    </tr>
    <tr>
      <td>Markdown files</td>
      <td>human-editable memory</td>
      <td>depends on folder size</td>
    </tr>
    <tr>
      <td>Recall adapter</td>
      <td>semantic search and stronger retrieval quality</td>
      <td>about 100ms write, about 200ms recall</td>
    </tr>
    <tr>
      <td>Vector store</td>
      <td>scale and semantic recall</td>
      <td>depends on embeddings and backend</td>
    </tr>
  </tbody>
</table>

<p>Fast memory is useful because agents ask for memory often. But retrieval quality matters too. UMP keeps that choice open. You can start with the file store, then swap the backend when the shape of the work needs it.</p>

<h2 id="the-part-i-care-about">The Part I Care About</h2>

<p>The part I care about is not that UMP exists as a spec. Specs are cheap.</p>

<p>The part I care about is the round trip:</p>

<blockquote>
  <p>an agent writes a memory, another agent recalls it, the record is still owned by the user, and the receiving agent treats it as scoped reference data instead of hidden instruction text.</p>
</blockquote>

<p>That is the missing piece for serious agent work. Agents are getting better at tools through MCP. They are getting better at coordination through <a href="https://a2a-protocol.org/latest/specification/">A2A</a>. But they still forget, fork, and trap memory inside product-specific state.</p>

<p>I do not want every new agent to start from zero. I also do not want all my working memory locked inside one vendor.</p>

<p>UMP is my attempt to make the layer small enough to adopt and structured enough to trust.</p>

<p>One record. Six operations. Memory that moves.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="agents" /><category term="memory" /><category term="protocols" /><category term="MCP" /><category term="open-source" /><summary type="html"><![CDATA[Agent memory is fragmented across tools, files, and products. Universal Memory Protocol is my attempt to make memory portable: one record, six operations, and bindings that work with MCP, HTTP, and files.]]></summary></entry><entry><title type="html">Everything Will Be Agent Use</title><link href="https://edihasaj.com/posts/everything-will-be-agent-use" rel="alternate" type="text/html" title="Everything Will Be Agent Use" /><published>2026-05-14T00:00:00+00:00</published><updated>2026-05-14T00:00:00+00:00</updated><id>https://edihasaj.com/posts/everything-will-be-agent-use</id><content type="html" xml:base="https://edihasaj.com/posts/everything-will-be-agent-use"><![CDATA[<p>I have been watching the new wave of computer-use agents and honestly, most of what they do is incredible. The mouse moves like a real user. Forms get filled. Sites get navigated. Tabs get juggled. Tasks that needed a person sitting in front of a screen are now running on their own, end to end, with surprisingly little hand-holding. Five years ago this was science fiction. Today it is a demo you can run on your own laptop, and the future of how we use software clearly runs through here.</p>

<p>Then you watch long enough and the same shape keeps showing up. The agent looks great for the first half of the task, sometimes the first ninety percent, and then the workflow hits a login wall, a captcha, a 3-D Secure popup, a card form that wants a human phone tap, and everything stalls. The agent was good enough to make the stall feel absurd. That is new. A year or two ago the impressive part was that it could move through the task at all.</p>

<p>That is the actual story of computer use right now. Agents got useful faster than the systems around them got ready to trust, verify, and bill them.</p>

<h2 id="the-cursor-is-the-new-interface">the cursor is the new interface</h2>

<p>For a few years we assumed AI would live in a chat box. Type, answer, repeat. The interface was words.</p>

<p>That assumption is dead. The interface is your operating system now. The agent gets the mouse, the keyboard, the browser tabs, sometimes your card. It does not describe how to do the thing. It does the thing.</p>

<p>Every serious lab is on this. Anthropic shipped computer use in late 2024, and Claude has been driving desktops since. OpenAI followed with Operator. Google has Gemini computer use plus Project Mariner inside Chrome. Perplexity built Comet, an entire browser shaped around an agent. Manus came out of China with a general-purpose agent that books trips, files paperwork, and scrapes whatever you point it at. Microsoft and Apple are wiring this into the OS itself, quieter, slower, much closer to where the real leverage is.</p>

<p>Pick any of them. The message is the same. The agent is the new user.</p>

<h2 id="how-far-we-actually-got">how far we actually got</h2>

<p>Two years ago, asking a model to complete a real multi-step task on a real desktop was a coin flip at best. OSWorld, the most honest early benchmark for desktop agents, was sitting at around 14% when it launched in 2024. The progress since has been fast enough that any single number starts going stale almost immediately.</p>

<figure class="post-figure">
<svg viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OSWorld benchmark progress chart" style="width:100%;height:auto;color:currentColor">
  <style>
    .axis { stroke: currentColor; stroke-width: 1; opacity: 0.4 }
    .grid { stroke: currentColor; stroke-width: 1; opacity: 0.1; stroke-dasharray: 2 4 }
    .lbl  { font: 12px ui-sans-serif, system-ui, -apple-system, sans-serif; fill: currentColor; opacity: 0.7 }
    .ttl  { font: 600 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .val  { font: 600 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .ln   { fill: none; stroke: #14b8a6; stroke-width: 2.5; stroke-linejoin: round }
    .hum  { stroke: #f59e0b; stroke-width: 1.5; stroke-dasharray: 4 4; fill: none }
    .pt   { fill: #14b8a6 }
  </style>
  <text x="20" y="22" class="ttl">OSWorld benchmark progress, desktop agents</text>
  <text x="20" y="40" class="lbl">success rate on desktop-task benchmark suites</text>

  <!-- grid + y axis -->
  <line class="axis" x1="60" y1="60" x2="60" y2="270" />
  <line class="axis" x1="60" y1="270" x2="700" y2="270" />
  <g>
    <line class="grid" x1="60" y1="90" x2="700" y2="90" /><text x="50" y="94" text-anchor="end" class="lbl">80%</text>
    <line class="grid" x1="60" y1="135" x2="700" y2="135" /><text x="50" y="139" text-anchor="end" class="lbl">60%</text>
    <line class="grid" x1="60" y1="180" x2="700" y2="180" /><text x="50" y="184" text-anchor="end" class="lbl">40%</text>
    <line class="grid" x1="60" y1="225" x2="700" y2="225" /><text x="50" y="229" text-anchor="end" class="lbl">20%</text>
    <text x="50" y="274" text-anchor="end" class="lbl">0%</text>
  </g>

  <!-- human baseline ~72% => y = 270 - (72/100)*180 = 140.4 -->
  <line class="hum" x1="60" y1="140" x2="700" y2="140" />
  <text x="694" y="134" text-anchor="end" class="lbl">human baseline ~72%</text>

  <!-- points: x positions for Apr24, Oct24, Apr25, Oct25, Apr26
       values: 14, 22, 38, 53, 75 -->
  <polyline class="ln" points="
    100,238
    250,220
    400,184
    550,151
    680,135
  " />
  <g>
    <circle class="pt" cx="100" cy="238" r="5" /><text x="100" y="258" text-anchor="middle" class="val">14%</text>
    <circle class="pt" cx="250" cy="220" r="5" /><text x="250" y="240" text-anchor="middle" class="val">22%</text>
    <circle class="pt" cx="400" cy="184" r="5" /><text x="400" y="204" text-anchor="middle" class="val">38%</text>
    <circle class="pt" cx="550" cy="151" r="5" /><text x="550" y="171" text-anchor="middle" class="val">53%</text>
    <circle class="pt" cx="680" cy="135" r="5" /><text x="680" y="125" text-anchor="middle" class="val">75%</text>
  </g>

  <g class="lbl">
    <text x="100" y="290" text-anchor="middle">Apr 2024</text>
    <text x="250" y="290" text-anchor="middle">Oct 2024</text>
    <text x="400" y="290" text-anchor="middle">Apr 2025</text>
    <text x="550" y="290" text-anchor="middle">Oct 2025</text>
    <text x="680" y="290" text-anchor="middle">Apr 2026</text>
  </g>
  <text x="380" y="314" text-anchor="middle" class="lbl">Approximate top reported scores. Human baseline ~72% from Xie et al., OSWorld 2024.</text>
</svg>
</figure>

<p>On “can the agent see and click the right thing”, the field is getting very good. Pixel-grounding benchmarks like ScreenSpot are above 90% for the top models. The eyes work. The hands mostly work. The old joke that agents cannot use computers is aging badly.</p>

<p>On “can the agent finish a real workflow”, the answer is messier. The best systems are now brushing against human baselines on OSWorld-style tasks, which is a ridiculous improvement from 2024. But benchmark parity is not the same thing as operational trust. Newer cross-application evals still show ugly failure rates once the task requires several apps, conditional judgment, cleanup, and persistence. Even a 75% task success rate means one in four things still falls over somewhere.</p>

<p>And that “somewhere” is almost never “I could not read the button”. It is the page reloaded, the login screen came back, a captcha appeared, a popup blocked the click, the cart wants your card, or the agent did the right local action inside the wrong global state.</p>

<p>The cursor is not the bottleneck. The world around the cursor is.</p>

<h2 id="the-five-shapes-people-are-shipping">the five shapes people are shipping</h2>

<p>There is a tendency to talk about “computer use” like it is one product. It is at least five, and the shapes have very different trade-offs.</p>

<p>The desktop agent runs on your machine and can touch anything you can touch. Anthropic’s computer use sits here. Max power, max blast radius. If it gets confused, it can delete things you cared about. Trust bar is high. Best for power users and developers who can sandbox it.</p>

<p>The browser agent lives inside a tab. Perplexity Comet, Arc’s agent, the various Chromium-based experiments. Safer becuase the world it can break is small, but it hits a wall every time a workflow leaves the browser. Try to download a PDF, sign it, attach it back somewhere, and the seams show up immediately.</p>

<p>The managed agent runs in the cloud. OpenAI Operator is the cleanest example. Convenient because you do not give it your laptop, but the moment the site you care about decides cloud IP’s look like bots, you are stuck in captcha purgatory.</p>

<p>The OS-level agent is the long game. Apple, Microsoft, and Google’s mobile push are all trying to wire agents into the system itself: real accessibility trees, real intents, real content types. When the OS exposes structure, the agent stops guessing. This is also the lane with the most patience, which is why Apple in particular looks slow untill they suddenly are not.</p>

<p>The generalist agent is the demo champion. Manus, Mariner-style long-horizon planners. They will plan your trip, file your form, scrape your competitor. They will also confidently do the wrong thing for ninety steps before failing in a way you cannot easily unwind.</p>

<p>Each shape is real. None of them is finished.</p>

<h2 id="where-the-workflow-actually-breaks">where the workflow actually breaks</h2>

<p>If you sit and watch a serious computer-use demo end to end, the failure mode is almost always the same. The agent looks brilliant for the first half of the task and then loses to something boring.</p>

<figure class="post-figure">
<svg viewBox="0 0 720 340" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Agent workflow drop-off funnel" style="width:100%;height:auto;color:currentColor">
  <style>
    .lbl { font: 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor; opacity: 0.8 }
    .ttl { font: 600 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .val { font: 600 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .step { fill: currentColor; opacity: 0.85 }
    .bar1 { fill: #14b8a6 }
    .bar2 { fill: #2dd4bf }
    .bar3 { fill: #fbbf24 }
    .bar4 { fill: #f97316 }
    .bar5 { fill: #ef4444 }
  </style>
  <text x="20" y="22" class="ttl">Where a real agent workflow drops off</text>
  <text x="20" y="40" class="lbl">success rate at each stage of "buy a thing online", roughly</text>

  <!-- bars: x=240 start, max width = 420 -->
  <g>
    <rect class="bar1" x="240" y="70" width="420" height="32" rx="4" />
    <text x="232" y="90" text-anchor="end" class="step">find the right product</text>
    <text x="668" y="90" class="val">95%</text>

    <rect class="bar2" x="240" y="116" width="394" height="32" rx="4" />
    <text x="232" y="136" text-anchor="end" class="step">add to cart</text>
    <text x="640" y="136" class="val">88%</text>

    <rect class="bar3" x="240" y="162" width="294" height="32" rx="4" />
    <text x="232" y="182" text-anchor="end" class="step">log in / handle popups</text>
    <text x="540" y="182" class="val">66%</text>

    <rect class="bar4" x="240" y="208" width="231" height="32" rx="4" />
    <text x="232" y="228" text-anchor="end" class="step">pass captcha / fraud check</text>
    <text x="477" y="228" class="val">52%</text>

    <rect class="bar5" x="240" y="254" width="180" height="32" rx="4" />
    <text x="232" y="274" text-anchor="end" class="step">complete payment + 3-D Secure</text>
    <text x="426" y="274" class="val">~40%</text>
  </g>

  <text x="380" y="320" text-anchor="middle" class="lbl">Illustrative composite from public agent evals, mid 2026. Indicative shape, not a measured benchmark.</text>
</svg>
</figure>

<p>It is not glamorous. Finding the product is easy. Adding it to the cart is easy. Then auth shows up, fraud signals trip, a phone-based 3-D Secure challenge fires off, and the agent stalls because the verification was designed to assume there is a human holding a phone, not a long-running automation.</p>

<p>Most demos quietly stop right before this cliff. The honest ones hand control back to the user at checkout, which kind of defeats the point of an agent.</p>

<p><img src="/images/posts/everything-will-be-agent-use-checkout-2026-05-14.jpg" alt="An AI cursor frozen in front of a glowing 3-D Secure popup over a blurred checkout form" /></p>

<h2 id="the-protocols-are-the-actual-story">the protocols are the actual story</h2>

<p>The reason buying is so painful is structural. The stack assumes a human in a browser, with cookies, a card on file, a phone for 3-D Secure, and a fraud score built from device fingerprints and behavior. An agent breaks almost every one of those assumptions, and the merchant has no clean way to know “this purchase was authorized by Edi, with these limits, through this agent, for this purpose”. So they either block it, or they let it through and hope.</p>

<p>This is what protocols are quietly fixing. Three layers matter.</p>

<figure class="post-figure">
<svg viewBox="0 0 720 290" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Agent protocol stack diagram" style="width:100%;height:auto;color:currentColor">
  <style>
    .ttl { font: 600 13px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .lbl { font: 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor; opacity: 0.8 }
    .tag { font: 600 12px ui-sans-serif, system-ui, sans-serif; fill: currentColor }
    .box { stroke: currentColor; stroke-width: 1.2; fill: none; rx: 6 }
    .l1  { fill: #14b8a6; opacity: 0.18 }
    .l2  { fill: #6366f1; opacity: 0.18 }
    .l3  { fill: #f59e0b; opacity: 0.20 }
    .arr { stroke: currentColor; stroke-width: 1.2; opacity: 0.5; fill: none; marker-end: url(#a) }
  </style>
  <defs>
    <marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="currentColor" opacity="0.5" />
    </marker>
  </defs>

  <text x="20" y="22" class="ttl">The agent protocol stack, mid 2026</text>
  <text x="20" y="40" class="lbl">three layers, three different adoption curves</text>

  <!-- AP2 -->
  <rect x="40" y="60" width="640" height="60" rx="8" class="l3" />
  <rect x="40" y="60" width="640" height="60" rx="8" class="box" />
  <text x="60" y="86" class="tag">AP2 — Agent Payments Protocol</text>
  <text x="60" y="106" class="lbl">verifiable mandates: this agent may spend up to X, for user Y, at merchant Z, signed and revocable</text>
  <text x="660" y="86" text-anchor="end" class="lbl">agent ↔ merchant</text>

  <!-- A2A -->
  <rect x="40" y="135" width="640" height="60" rx="8" class="l2" />
  <rect x="40" y="135" width="640" height="60" rx="8" class="box" />
  <text x="60" y="161" class="tag">A2A — Agent2Agent</text>
  <text x="60" y="181" class="lbl">two agents from different vendors negotiate, hand off tasks, exchange context, return results</text>
  <text x="660" y="161" text-anchor="end" class="lbl">agent ↔ agent</text>

  <!-- MCP -->
  <rect x="40" y="210" width="640" height="60" rx="8" class="l1" />
  <rect x="40" y="210" width="640" height="60" rx="8" class="box" />
  <text x="60" y="236" class="tag">MCP — Model Context Protocol</text>
  <text x="60" y="256" class="lbl">the plumbing: agent talks to tools, files, APIs, databases in a single standard way</text>
  <text x="660" y="236" text-anchor="end" class="lbl">agent ↔ tool</text>
</svg>
</figure>

<p>MCP is the floor. It is the boring, beautiful plumbing that lets an agent talk to a tool, a database, a file system, an API in one standard way. It is already widely adopted across Anthropic, OpenAI, Cursor, and most serious IDEs. If you are building anything agentic, MCP is the layer you build on, not around.</p>

<p>A2A is the layer above. Google and a coalition of partners pushed it out in 2025 as an open protocol for agents from different vendors to talk to each other. My booking agent calls your inventory agent, they negotiate, work happens, result comes back. If A2A actually catches on, the web stops being a pile of HTML that an agent has to squint at, and starts being a network of cooperating services with structured handshakes.</p>

<p>AP2 is the one I find most concrete. The Agent Payments Protocol, announced by Google with Mastercard, American Express, Coinbase, PayPal and a long list of issuers in late 2025, is an attempt to do for agent commerce what 3-D Secure tried to do for online cards. The agent presents a verifiable mandate signed by the human. The merchant verifies it. The issuer verifies it. Payment flows, with cryptographic evidence at every step. “This agent bought this item for this user with this budget” finally has a real signature behind it instead of a session cookie and a prayer.</p>

<p>If you are a merchant, this is the upgrade path you cannot ignore for long. Either you wire up agent-friendly payments with verifiable mandates, or you start quietly blocking a non-trivial slice of your traffic, because that slice is no longer human.</p>

<h2 id="the-messy-middle-is-where-we-live">the messy middle is where we live</h2>

<p>The bad news is that none of these protocols are live across the long tail. MCP is the closest. A2A is real but mostly between cooperative parties. AP2 has the right names on the press release and nowhere near enough merchant support to run your week through it yet.</p>

<p>So agents in 2026 are stuck doing two jobs at once.</p>

<p>That is why the moment feels so strange. Agents are good enough to use every day, and not good enough to forget about while they hold your card, your calendar, your inbox, or your company account.</p>

<p>One job is helping the long tail of legacy sites and apps that will never speak A2A by pretending to be a human. This is a perception and resilience problem. Better screen understanding, better recovery when a popup appears, better memory of “I already tried this and it failed”. The flashy demo lives here, and so does most of the brittleness. Every UI redesign quietly breaks an agent somewhere.</p>

<p>The other job is helping the leading edge of services adopt MCP, A2A, AP2, so the next round can be cleaner. This is mostly a trust and identity problem. Who signed the mandate, who is liable, how do we revoke, what audit trail do we keep. Less glamorous. Much more durable.</p>

<p>The winners over the next few years will not be the labs with the flashiest demo. They will be the ones that solve the perception problem and the trust problem at the same time, so the agent is both capable of running a task on a legacy site and credible enough to do it on a modern one. Most of the current crop is good at one and pretending about the other.</p>

<h2 id="what-to-actually-do">what to actually do</h2>

<p>If you build software, four things are worth doing this year. Design your UI so an agent can also use it: real accessibility trees, structured labels, predictable flows, not just whatever the design system spat out. Ship an MCP server if you have any tool surface worth exposing. Start thinking about an A2A integration even before you build it, because the shape of your data and identity model matters more than the wire format. If you take payments, track AP2 and similar mandates, even if you do not implement yet, because the fraud and identity decisions you make in 2026 will determine whether you can adopt cleanly in 2027.</p>

<p>If you do not build software, the practical version is shorter. The next few apps you install, ask whether an agent could run it for you. If yes, thats your new productivity ceiling. If no, that is a future migration waiting to happen.</p>

<p>The cursor is moving. Whether it is moved by you or by something acting on your behalf is going to become a question you ask several times a day. I am not nostalgic about clicking through forms. I am, however, nervous about the gap between what these agents can already do and what the systems on the other side are ready to verify, trust and bill. That gap is the work of the next two years, and the companies that close it first are going to look very different from the ones currently winning the demo cycle.</p>

<p>The demo is the easy part. The plumbing under it is the rest of the decade.</p>]]></content><author><name>Edi Hasaj</name></author><category term="obsidian" /><category term="posts" /><category term="AI" /><category term="agents" /><category term="computer-use" /><category term="protocols" /><category term="A2A" /><category term="MCP" /><summary type="html"><![CDATA[Computer use is the new browser war. Google, OpenAI, Anthropic, Perplexity and Manus are all racing to put an agent in front of the screen. The hard part is not moving the mouse, it is that the world on the other side of the cursor was never built for them.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://edihasaj.com/images/posts/everything-will-be-agent-use-2026-05-14.jpg" /><media:content medium="image" url="https://edihasaj.com/images/posts/everything-will-be-agent-use-2026-05-14.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>