PageSourceSearch

https://www.omnara.com/assets/immutable/blog-By61I5cd.js

js omnara.com collected 2026-10-03 23:36:33 UTC 83,025 bytes, 868 lines download raw bytes

1const g=`---
2title: "The Mobile Coding Landscape"
3subtitle: "An objective comparison of Omnara, alternatives, and the shift toward untethered development"
4seoTitle: "Omnara vs Claude Code, Devin, and More: Mobile Coding Agents in 2026"
5seoDescription: "Compare 5 ways to run coding agents from your phone, from terminal clients and cloud sandboxes to Omnara's local-first hybrid approach."
6date: "2026-03-02"
7author: "Ishaan Sehgal"
8thumbnail: "./assets/thumbnail.png"
9draft: false
10archiveNote: "This comparison covers Omnara's previous remote coding product and is retained for historical reference. Omnara is now an open-source managed-agent platform."
11---
12
13The mobile coding interface landscape is evolving quickly. As coding agents become more capable, developers are looking for ways to manage them from outside the terminal. Today, there are five primary approaches to controlling coding agents from mobile. Here is a breakdown of how they compare.
14
15## 1. Mobile Terminal Clients & Emulators
16
17Since popular agents like Claude Code and Codex are terminal-native, the most direct way to use them on your phone is via a mobile terminal emulator like Blink, Termius, or a browser-based terminal. You connect back to your machine using whatever networking stack you prefer, whether it's a public IP + SSH, a Tailscale mesh network, or a remote web shell.
18
19To make this viable for mobile, most setups include \`tmux\` or \`screen\` to keep the agent process alive on the host machine even if the mobile app disconnects. A typical stack looks like Tailscale for networking, SSH for auth, tmux for session persistence, and a terminal emulator like Termius running the agent CLI.
20
21**The Appeal:** It is the "purest" form of control. You have total environment fidelity because you are interacting directly with your own machine. Your secrets, dependencies, and local workflows are exactly where you left them.
22
23**The Reality Gap:** The terminal is a high-bandwidth tool being forced through a low-bandwidth straw. Navigating tmux panes on a 6-inch screen is a "fat-finger" nightmare. Agents often output hundreds of lines of logs, tool calls, and/or file changes. Scrolling through a raw terminal buffer on mobile to find where an agent got stuck is slow and mentally taxing.
24
25Moreover, since these are general-purpose emulators, they lack coding-specific UX on mobile. There is no native way to view rendered Markdown, see side-by-side diffs, or easily manage multiple worktrees and sessions. You're essentially trying to manage your agents through an interface designed to be used on your laptop.
26
27---
28
29## 2. General Computer Assistants
30
31*(OpenClaw, Zo.Computer, Manus)*
32
33This category represents a shift from "chatbots" to "AI-powered computers." You interact with a persistent, stateful environment, either a local machine or a dedicated cloud server (Zo, Manus), via a simple chat interface like WhatsApp, Telegram, or a web/desktop dashboard.
34
35These assistants have full "Computer Use" capabilities. They don't just answer questions; they control a file system, run terminal commands, and can even spin up their own sub-agents to handle background research while you're away.
36
37**The Appeal:** The friction to start and the quality of integrations vary, but the core idea is simple: if you can send a text, you can manage your server. It feels like having a digital assistant who has 24/7 access to your work computer. Because they are generalists, they can handle non-coding tasks too, like monitoring a database or summarizing your emails.
38
39**The Reality Gap:** The "Generalist" UI eventually hits a wall when it comes to coding agents. Chatting with a computer is great for high-level intent ("Deploy the latest build"), but it's a "leaky" container for the specialized needs of a developer. Because mobile developer UX is not the primary audience, you lose essential primitives: there's no native way to select models or harnesses, spin up new sessions, create worktrees, or interactively view code changes. You're trying to manage the needs of a developer through a general-purpose interface.
40
41---
42
43## 3. Cloud-Based Environments
44
45*(Claude / Codex Web, Cursor Background Agents, Devin, Replit, Lovable)*
46
47This category moves execution entirely off your local hardware to a remote cloud environment. Today, this manifests in two distinct workflows.
48
49**Ephemeral Agents:** Tools like Cursor Background Agents, Claude, and Codex Web spin up "disposable" containers, cloning your GitHub repo for quick tasks, but destroying the environment when the session ends.
50
51Tools like Devin go a step further by requiring an initial "devbox" setup that gets snapshotted. This VM image is used as the baseline for every session. However, just like the others, manual changes (like an \`npm install\`) in one session won't persist across sessions unless codified in your persistent setup steps.
52
53**End-to-End Platforms:** Tools like Replit and Lovable host the entire application, acting as self-contained ecosystems where the agent builds, tests, and deploys in one place.
54
55**The Appeal:** Zero-friction starts. You can go from idea to running app on a plane with just a phone. Since the agent runs in the cloud, it doesn't matter if your laptop is closed, dead, or non-existent. It's the ultimate "laptop-off" workflow for greenfield prototyping.
56
57**The Reality Gap:** These solutions often feel like "disconnected worlds" from your actual development machine. While great for new projects or one-off changes, cloud sandboxes struggle with complex, pre-existing local setups. If you have a dev environment that isn't easily replicable (say due to specific secrets, dependencies, or workflows) the agent is going to have a hard time.
58
59For PR-based agents (like Cursor background agents and Devin), the feedback loop is asynchronous. You aren't "steering" the code as it's written; you are reviewing a finished product. If the agent makes a wrong architectural turn in the first ten minutes, you might not catch it until the PR lands an hour later, leading to a frustrating loop of comments and re-runs between disconnected environments.
60
61---
62
63## 4. Remote Control Apps
64
65*(Claude Code Remote Control / HappyCoder)*
66
67Instead of moving your code to the cloud, these tools "remote control" into a session already running on your laptop.
68
69The major differentiator here compared to a terminal client is the **Native Mobile Interface**. You aren't just looking at a terminal emulator; you are using a purpose-built mobile app meant to be used for agentic interaction. HappyCoder, for example, wraps the CLI in an encrypted relay so you can text your local agent through a dedicated interface.
70
71**The Appeal:** This is the cleanest bridge between your desk and the world. The massive benefit over a terminal client is the UX Leap. You get native mobile primitives, chat, structured diffs you can actually read, and easy-to-tap buttons to interrupt or approve tasks, instead of fighting with a virtual keyboard in a tmux pane. There's no onboarding friction or re-installing dependencies.
72
73**The Reality Gap:** The "Closed Laptop" problem. Because the session lives on your physical hardware, it inherits all of its fragility. Close the lid, it pauses. Let the laptop sleep, it drops. If your Wi-Fi blips, the agent hangs.
74
75You aren't truly untethered; you're just stretching a cord. Newer entries like Claude Code Remote Control are still in early stages and face reliability issues, with developers reporting failed sessions. You're effectively babysitting a local process from a distance. It's a meaningful step forward, but it doesn't yet solve the core problem: true mobility requires the agent to keep running even when your laptop doesn't.
76
77---
78
79## 5. The Hybrid Approach
80
81*(Omnara, Claude Teleport + Remote Control)*
82
83The hybrid model attempts to reconcile two competing truths: developers want full fidelity to their local environment, but agents need to survive beyond a single device.
84
85Instead of choosing between a local session that dies when your laptop closes or a cloud session that lacks your local setup, these tools attempt to synchronize state between both.
86
87**Omnara's Implementation:** Omnara is a remote control for your local machine, but with a built-in option to migrate to the cloud. Upon enablement, Omnara syncs your session state, including uncommitted changes, worktrees, and conversation context. If your laptop closes, your Wi-Fi drops, or you walk away from your desk, the session doesn't hang or die. It prompts you to transition to a cloud-backed continuation, allowing your agent to keep running while you're untethered.
88
89**The Teleport Alternative:** Claude Code's \`/teleport\` feature offers a partial implementation of this idea. It allows a local session to be moved to the cloud, but the transfer must be initiated manually from the laptop before leaving (it cannot be triggered from a phone). The local machine must also remain active during the transition and meet certain environment constraints.
90
91This makes \`/teleport\` useful for planned handoffs, though it does not yet provide seamless, device-independent continuity.
92
93**The Goal:** A true hybrid system aims to combine:
94
95- Local environment fidelity
96- Purpose-built mobile UX
97- Persistent execution independent of any single device
98
99The aspiration is simple: your agent continues building whether or not your laptop is open.
100
101**The Reality Gap:** Seamlessly synchronizing state between a local machine and the cloud is technically challenging. Because real-world codebases involve complex worktrees, uncommitted changes, and active conversation contexts, maintaining a "zero-friction" handoff is an incredibly hard engineering problem.
102
103While this architecture is designed to keep you in flow without being tethered to a running local process or stuck in an asynchronous PR dance, the complexity of state-syncing means technical gaps can still occur during transitions. It remains an early-stage solution as the industry works to bridge the divide between local fidelity and cloud availability.
104
105---
106
107## The Landscape at a Glance
108
109| Core Dimension | DIY SSH | General Assistants | Cloud Envs | Remote Control | Hybrid (Omnara) |
110| --- | --- | --- | --- | --- | --- |
111| Execution Lives | Local | Cloud/Local | Cloud | Local | Local + Cloud |
112| Survives Closed Laptop | ❌ | ✅ (if cloud) <br> ❌ (if local) | ✅ | ❌ | ✅ |
113| Environment Fidelity | ✅ | Varies | ❌ | ✅ | ✅ |
114| Mobile Optimized UX | ❌ | ❌ (for coding agents) | ✅ | ✅ | ✅ |
115
116---
117
118## Where This Is Heading
119
120Every category above is converging toward the same underlying question: *Should agent execution be tied to a specific device?*
121
122- Terminal clients prioritize fidelity.
123- Cloud environments prioritize availability.
124- Remote control prioritizes UX.
125- Hybrid systems attempt to reconcile all three.
126
127As agents become more autonomous and sessions run longer, the ability to monitor, steer, and continue work without being physically present is shifting from a convenience to a necessity.
128
129The landscape is still evolving, but one thing is increasingly clear:
130
131> **Mobility is no longer about location. It’s about continuity.**
132
133---
134
135## Try Omnara
136
137If this perspective resonates, try Omnara for free.
138
139Install Omnara in your terminal:
140
141\`\`\`bash
142curl -fsSL https://omnara.com/install.sh | bash
143\`\`\`
144
145Then run Omnara in the directory you want to work from:
146
147\`\`\`bash
148omnara
149\`\`\`
150
151Join the [Discord](https://discord.gg/Dc46sYk6e3) to ask questions, share feedback, and discuss ideas with the community.
152
153— Ishaan, Kartik, and Christian
154`,m=`---
155title: "Portability Problems: How Omnara Syncs Coding Agent State Across Machines"
156subtitle: "The tech behind our cloud sandboxing features"
157seoTitle: "Omnara's AI Cloud Sandbox Syncing"
158seoDescription: "The tech behind Omnara's cloud sandbox syncing for Claude Code and Codex"
159date: "2026-04-06"
160author: "Christian Sparks"
161thumbnail: "./assets/hero.png"
162draft: false
163archiveNote: "This post covers sandbox syncing in Omnara's previous remote coding product and is retained for historical reference. Omnara is now an open-source managed-agent platform."
164---
165
166You kick off a codex xhigh refactor on your laptop. 15m later, you realize you need to leave, and codex is only halfway done. You want to pick it up on your phone with the same code changes and conversation. This is an obviously reasonable request. It is also something no one has built well.
167
168There are a litany of inferior approximations. You can work entirely from a persistent VM. But you must accept the latency and environment management pain. You can resume via teleporting. But you need to have [completed this checklist ahead of time](https://code.claude.com/docs/en/claude-code-on-the-web#requirements-for-teleporting). You can use remote control, but your laptop needs to stay open.
169
170Each solution partially solves the problem. However, the solution isn’t unachievable\\! A combination of resumable chat sessions, smart code checkpointing, and sandboxes solve the portability problem.
171
172## Resuming chat sessions
173
174Let’s start the easy part: a primer on how most LLM harnesses interact with a /completions (or /responses or /messages) API. 
175
176Each time you send a message to a coding agent, this results in a chat turn. Each chat turn consists of one or more steps. There is usually one step per tool call.
177
178![Chat turn diagram](./assets/diagram1.png)
179
180The above consists of 1 turn, and 3 steps (one for each tool call)
181
182The coding agent harness saves some state of the conversation at each turn because it needs to make a subsequent API call until the agent has completed its generation. In Claude Code, this is just a JSONL session file that looks roughly like this:
183
184\`\`\`jsonl
185{
186    "uuid": "<uuid>",
187    "timestamp": "2026-03-13T22:18:39.490Z",
188    "cwd": "<cwd>",
189    "sessionId": "<uuid>",
190    "gitBranch": "<branch>",
191    "message": {
192        "role": "user",
193        "content": [
194            {
195                "type": "text",
196                "text": "When we create a worktree..."
197            }
198        ]
199    },
200}
201{
202    "uuid": "<uuid>",
203    "timestamp": "2026-03-13T22:18:42.562Z",
204    "cwd": "<cwd>",
205    "sessionId": "<uuid>",
206    "gitBranch": "<branch>",
207    "message": {
208        "model": "claude-opus-4-6",
209        "id": "<id>",
210        "type": "message",
211        "role": "assistant",
212        "content": [
213            {
214                "type": "text",
215                "text": "\\n\\nI'll start by researching the codebase..."
216            }
217        ],
218        "stop_reason": null,
219        "stop_sequence": null,
220        "usage": {
221            "input_tokens": 3,
222            "cache_creation_input_tokens": 19537,
223            "cache_read_input_tokens": 0,
224            "cache_creation": {
225                "ephemeral_5m_input_tokens": 19537,
226                "ephemeral_1h_input_tokens": 0
227            },
228            "output_tokens": 2,
229            "service_tier": "standard",
230            "inference_geo": "global"
231        }
232    }
233}
234\`\`\`
235
236In Omnara, we persist those messages to our database. As long as we can reconstruct that session file from the messages in our database, we can resume the chat later on any machine\\! The way Claude Code stores messages isn’t a public interface, but we do a best effort reconstruction of the session file.
237
238## Code checkpointing: weird git commands that you probably shouldn’t use
239
240Now, we need to recover the code at some point in time in the conversation. Conceptually, this is simple. What if, after every write action (i.e. any Edit, Bash, or other writable tool call), we save a checkpoint of the codebase.
241
242![Checkpoint diagram](./assets/diagram2.png)
243
244Most coding agents have support for hooking into tool call actions by default. The harder part is actually constructing the checkpoints, even if the user is already using source control.
245
246We need to reconstruct:  
247- The user’s working directory  
248- The git repository itself (and the current ref the user is on)
249
250The key primitives here are [\`git write-tree\`](https://git-scm.com/docs/git-write-tree) and [\`git commit-tree\`](https://git-scm.com/docs/git-commit-tree).
251
252This requires us to capture 4 things:  
2531. **headCommit** -- the current HEAD SHA (\`git rev-parse HEAD\`)
2542. **headRef** -- the current branch name, or null if detached (\`git symbolic-ref --short HEAD\`)
2553. **indexTree** -- staged changes as a tree (\`git write-tree\`)
2564. **worktreeTree** -- the working directory as a tree, including untracked files
257
258\`\`\`typescript
259private async captureWorktreeTree(): Promise<string> {
260    const tempIndexPath = \`\${gitDir}/omnara-temp-index-\${uniqueId}\`;
261    const gitEnv = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
262
263    // Step 1: Copy real index -> temp index (preserves tracked files, even gitignored ones)
264    await fs.copyFile(realIndexPath, tempIndexPath);
265
266    // Step 2: Update temp index with working tree modifications to tracked files
267    execSync('git add --update', { env: gitEnv });
268
269    // Step 3: Add NEW untracked files (respecting .gitignore)
270    execSync('git add .', { env: gitEnv });
271
272    // Step 4: Write the temp index as a tree object
273    const result = execSync('git write-tree', { env: gitEnv });
274    return result.trim();
275}
276\`\`\`
277
278Then, with the worktreeTree, we can commit with \`commit-tree\` and attach important metadata to the commit to reconstruct the repository at some point in time.
279
280## Cloud storage
281
282There’s one final piece: uploading the conversation and code to the cloud so it can be pulled down later on a different machine. For the conversation, that’s easy -- we already store that data in our database. The code part is only marginally harder, since git is designed to bundle and push changes remotely. Though we don’t want to push directly to a user’s git remote, as that causes clutter and risks pushing unintentional changes (particularly dangerous if we accidentally push a user’s .env). Our solution is to manually upload [git bundles](https://git-scm.com/docs/git-bundle), which package git objects into a single file that can be transferred without a live remote. 
283
284For each checkpoint we create two bundles:  
2851. headCommit  
286    1. this is a bundle containing the current head commit for the checkpoint  
287    2. this will contain the majority of the repository’s commit history  
2882. incremental checkpointRef bundle  
289    1. This contains the incremental changes from headCommit
290
291Using two bundles, one being incremental helps us save a little bit on storage upload times and storage costs. Technically, the optimal solution would be hosting a git remote per user workspace to dedupe git objects, however, we just end up deduping the headCommit. It's content-addressed by the HEAD SHA, so if a bundle for that commit already exists we skip the upload. We store everything in object storage using presigned URLs (we use R2) which lets us upload from the user's own machine or our managed cloud sandboxes. Even though we’re inefficient with storage, object storage is cheap, so it’s not a big deal if we’re duplicating a lot of data. This significantly reduces complexity, since we don’t need to expose full git remote functionality to the user.
292
293Then, once uploaded, we modify a pointer for latestSyncedCheckpoint, which is a parameter for each worktree. Our abstraction for a single copy of a workspace is based on worktrees, so you may move worktrees between remote and local (and have any number of chats grouped with a worktree)
294
295## Sandboxing (and why cold start doesn't matter that much)
296
297When a user resumes a session remotely, we start a session using a sandbox. We use Cloudflare’s Sandbox SDK, but you could use any other sandbox provider. It just needs a filesystem and coding agent harness. We initialize the repo with the bundles we created above, and then seed the .claude/ or .codex/ folder with our session file reconstructed from the messages in our database.
298
299One aside: we don't use much of the richer functionality of these sandbox SDKs (CF Sandbox SDK, E2B, Daytona). We mostly treat it as a dumb keyed sandbox, because all of our setup is upfront. Most of these sandbox libraries want you to manage lifecycle through the SDK. We already have a daemon process that handles our application lifecycle, so we don't end up using most of those features. Here's roughly what the setup process looks like:
300
301\`\`\`typescript
302// workers API
303app.post('/worktrees/:worktree_id', /* auth & validation middleware */ async (c) => {
304  const sandbox = getSandbox(env.Sandbox, worktreeId, { sleepAfter: "4h" });
305  await sandbox.start({ entrypoint: ["./worktree-entrypoint.ts"], envVars: buildEnvVars(), enableInternet: true });
306  return c.ok()
307})
308\`\`\`
309
310\`\`\`typescript
311// entrypoint inside container
312async function entrypoint() {
313  const config = validateConfig(process.env)
314
315  // initialize and create llm configs, like ~/.claude and ~/.codex folders
316  writeLLMCreds(config)
317
318  // run background daemon, this allows us to start coding agent sessions on our machine
319  const daemonPid = spawn("omnara daemon run-service");
320
321  // setup github CLI creds and git push credentials
322  if (config.github) {
323    setupGithub(config.github)
324  }
325
326  if (config.checkpointId) {
327    // clone from workspace bundles (existing checkpoint id)
328    await exec(\`omnara workspace load --checkpoint-id \${config.checkpointId}\`)
329  } else {
330    // clone from scratch (not starting from a checkpoint)
331    await gitClone(config)
332  }
333
334  // write file that indicates to the daemon that the workspace is loaded, so
335  // we don't start sessions before the session has started
336  writeFile(\`/tmp/.omnara-workspace-loaded\`)
337}
338\`\`\`
339
340Many sandbox providers fixate on cold start time, but in our experience, it doesn't matter much. The marginal difference between 500ms and 2s is negligible if your sandbox is stateful. The cold start time is almost always going to be dominated by the time it takes to bootstrap code and configs. Currently, our TTI for our sandboxes is around 8-10s. Under 2s of that is the sandbox itself initializing; the remaining 6-8s is all of our application logic. This includes resolving the git checkpoint, loading session files, and starting the \`omnara\` background process.
341
342## Try it out
343
344The hard part of running a sandbox is the state. We've figured out the hard parts of state sync, so you don't have to figure it out yourself. Try it out by signing up at omnara.com!
345`,p=`---
346title: "Serverless Agents"
347subtitle: "No machine owns the loop"
348seoTitle: "Serverless Agents: No Single Machine Owns the Loop | Omnara"
349seoDescription: "No single machine should own the agent loop. Put agent continuity in a persistent control plane, make the loop serverless, and expose machines as tools."
350date: "2026-07-23"
351author: "Ishaan Sehgal"
352thumbnail: "./assets/thumbnail.png"
353draft: false
354---
355
356Start a coding agent on your laptop. Give it an hour-long task. Close the lid five minutes in.
357
358The agent dies.
359
360We spent two decades moving compute into the cloud, then built intelligence that can work around the clock, only to chain it to devices that sleep.
361
362Today this is normal. The agent loop and the developer's device share one lifecycle. It's why the quarter-open laptop meme exists: lids propped open overnight so an "asynchronous" agent doesn't die.
363
364![A tiny orange hand props a laptop lid open so the agent doesn't die](./assets/quarter-open-laptop-meme.png)
365
366It won't stay normal. Laptops will still matter, but as interfaces to agents, not where their loops run. In fact, no single machine should own the loop.
367
368## How Agents Inherited the Laptop
369
370When Claude Code launched in early 2025, local-first was the obvious choice. The model ran in the cloud, but the harness stayed beside the code: holding the conversation, deciding what happened next, invoking tools, waiting for results. The agent and its execution environment became one unit.
371
372![The agent loop, working tree, tools, and credentials all live inside one laptop or VM, with only the model API in the cloud](./assets/one-machine-one-lifecycle.png)
373
374Everything that followed copied the shape. Codex, Gemini, Cline, Grok, OpenCode — every coding agent shipped a local-first CLI. Agentic IDEs like Cursor and Zed told the same story. Frameworks made it easy to build your own local-first loops, and when people wanted those agents accessible to others, they did the natural thing: rented a machine in the cloud and ran the same loop there. The architecture didn't change. The laptop just moved.
375
376None of this was a mistake. The agent lived on the machine because that was where the developer already was, and there was no compelling reason yet to separate the two. A natural starting point hardened into the default.
377
378But that default carried an assumption.
379
380## Agents Break the Synchronous Assumption
381
382For most of programming history, the only thing that could write code was us. An assumption settled in so deeply that nobody thought to name it: when the programmer stops, the programming stops. Every tool we built respected it. Even cloud development environments — always-on compute, sitting in a datacenter — mostly just gave the programmer another place to type. The session still ended when the human left.
383
384Agents broke that assumption.
385
386An agent can investigate a failure, wait on CI, react to an external event, ask for approval, and keep going long after the interaction that started it has ended. Its useful lifetime is no longer bounded by a terminal session or a person at a keyboard.
387
388That's the step function: useful programming continuing after the programmer leaves. This renders the local loop a constraint rather than a convenience. There is no reason for the agent to inherit the lifecycle of the human’s device.
389
390## Separating Continuity From Execution
391
392If the agent shouldn't inherit the device's lifecycle, its continuity has to live somewhere else.
393
394[*The Log Is the Agent*](/blog/the-log-is-the-agent) argued that an agent's continuity lives in its durable event history, not in any particular machine or process. Every message, tool call, and decision is appended to a log, and a fresh executor can reconstruct the agent from that log alone.
395
396That property demotes the machine. If reconstruction is always possible, no laptop — no continuously running process anyw
396here — needs to own the loop.
397
398Which suggests a cleaner boundary.
399
400- **The control plane** owns the durable agent: its event history, pending work, model calls, approvals, and lifecycle.
401- **The execution plane** holds the environments where actions land: a laptop with a working tree, a cloud machine running tests, a host inside a private network.
402
403![The control plane holds the durable log and ephemeral workers; the execution plane spans laptops, sandboxes, and private machines, connected by durable commands and result events](./assets/control-execution-planes.png)
404
405Where the control plane runs is a deployment choice. It can be managed, self-hosted in a VPC, or operated on private infrastructure. What matters is that it persists independently of whichever device the user happens to be holding.
406
407The execution plane can span everything from your laptop to cloud sandboxes, containers, VMs, and GPUs. The key inversion is that machines are now exposed to the agent as tools. And it's up to the agent to decide where to run what, be it in the control plane itself when no dedicated machine is needed, or in whichever execution environment it finds best fits the work. (More on the execution plane in its own post.)
408
409![The control plane routes actions to machines as tools: edit files on the laptop, run tests in a cloud sandbox, query an internal service on a VPC host](./assets/machines-are-tools.png)
410
411Separate the lifecycles this way, and the loop itself can go serverless.
412
413## What a Serverless Agent Actually Is
414
415A message arrives. An ephemeral worker claims the agent, reconstructs its state from the log, asks the model for the next action, records it, and disappears. When a tool result, timer, or human reply lands later, a different worker picks up where the last one left off.
416
417Simplified example:
418
419\`\`\`python
420# Runs on any worker, whenever an event lands
421def advance(agent_id, event):
422    with temporary_lease(agent_id):
423        append_to_log(agent_id, event)
424        state = reconstruct_from_log(agent_id)
425        action = model.next(state)
426        append_to_log(agent_id, action)
427        dispatch(action)          # fire-and-forget
428    # worker exits - the agent persists in the log
429\`\`\`
430
431"Serverless" doesn't mean no computer runs the loop. It means no particular computer owns it. Workers are temporary executors over durable state. An agent can therefore remain alive while zero workers are running. It may simply be waiting on CI, an approval, a scheduled time, or another external event.
432
433![A timeline where ephemeral workers blink in and out above a continuous durable agent log: alive the whole time, compute only consumed when there's work](./assets/alive-not-running-timeline.png)
434
435Local-first architecture also obscured how little agent work requires a dedicated machine. A chat turn is the clearest example: a message arrives, the model responds, and the agent waits. The same is true when an agent calls an API, browses the web, invokes an integration, or asks a human a question. None of those actions requires a Linux box assigned to the conversation.
436
437A machine enters only when the agent needs something the control plane can't provide: an operating system, a working tree, a private network, specialized hardware, or local data. Binding every agent to a machine mistakes an occasional execution requirement for a permanent dependency.
438
439## The New Role of the Device
440
441The most compelling part of this shift is that the user experience can remain exactly as it is today. Nobody has to abandon the terminal, replace their IDE, or move every repository into a cloud sandbox. The interface that made local agents useful stays.
442
443You open a terminal inside a repository, type a request, watch commands run against local files, and approve changes in the same place. The agent uses the same working tree, tools, and credentials because the laptop remains part of the execution plane.
444
445What changes is the role of the device. It is no longer the container for the agent; it is a client connected to a durable harness running elsewhere. When that harness needs to read a file or run a local command, it routes the action to the laptop and records the result back into the agent's history. From your perspective, the command ran locally. From the system's perspective, the local machine was just one execution environment utilized by the agent.
446
447The difference appears when the device disappears. Close the laptop and work that depends on its local files may pause, but the agent remains in the control plane. If the required state is available elsewhere, it can continue there or reproduce the environment on a new machine. And when the laptop returns, the same agent can resume using it again. The machine, it turns out, was never special: the agent can provision machines as the task demands, spread work across several at once, release them when it's done, and carry on as machines come and go.
448
449![The control plane provisions Linux, Windows, and macOS VMs to run a test suite, collects results into the durable log, and releases machines when done](./assets/machine-lifecycles-as-tools.png)
450
451And because the agent is no longer contained by one interface, it can be reached through many. Start a task from a laptop, approve a question from a phone, follow its progress in Slack, and review the result from another machine. Several people can also steer the same durable session without sharing access to one developer’s terminal.
452
453In this setup, the laptop’s role resolves cleanly: an interface for the user and an execution environment for the agent. No longer the place the agent lives.
454
455## Properties That Fall Out
456
457Once continuity no longer depends on a particular device or process, several properties follow.
458
459**Availability.** Agents spend most of their lives waiting — on tests, approvals, timers, APIs, people. In a local architecture, every wait is an uptime requirement for some machine. In a persistent control plane, waiting is just durable state. When new work arrives, a worker reconstructs the agent and continues. The agent doesn't need to run continuously to be continuously available.
460
461**Resilience.** Because no worker owns the agent, workers are allowed to fail. Another reconstructs the same state from the log and picks up. A laptop can disconnect without erasing the agent's objective, history, or pending work. This doesn't magically migrate an uncommitted working tree or a running OS process, but one executor's failure no longer becomes the agent's failure. The process died; the agent didn't.
462
463**Scalability.** The platform no longer needs one continuously running process per agent. A shared worker pool advances whichever agents currently have work, and the two kinds of capacity scale on separate curves:
464
465\`\`\`
466worker capacity   scales with concurrent agent advancement
467machine capacity  scales with active operating-system work
468\`\`\`
469
470**Efficiency.** A persistent agent no longer implies a permanently allocated sandbox. Imagine 1,000 durable agents where only 5 percent need a shell at any given moment: the control plane holds 1,000 histories, but the execution plane only needs capacity for ~50 active workloads plus headroom. That’s the outcome of separating persistent identity from active execution. An idle agent consumes durable storage rather than 
470idle machines. And specialized hardware can attach for one stage of a task instead of for the agent’s entire lifetime.
471
472Now some environments should remain warm. A large repository, a loaded model, an expensive-to-rebuild cache, or simply an actively used workspace can all justify keeping a machine alive. The difference is that persistence becomes an explicit choice rather than a side effect of the agent existing.
473
474And these are only a few examples. Treat agents as durable systems rather than device-bound processes, and they gain the properties we expect from serverless infrastructure.
475
476## Who Owns the Loop
477
478The industry is already moving in this direction. Anthropic’s [Managed Agents](https://www.anthropic.com/engineering/managed-agents) separates the harness advancing the agent from the sandbox where code executes. Inference can begin before a container is provisioned. Ramp, Stripe, Sentry, Coinbase, and others have built internal background agents around related separations.
479
480But notice what none of them offer: ownership. The internal agents are internal. The managed offerings run one provider's models on that provider's infrastructure: not model-agnostic, not open-source, not self-hostable.
481
482We think that has to change, because an agent is the most intimate software you'll ever run. To be useful, it needs your personal data, your company's data, your workflows, your decisions. [*The Log Is the Agent*](/blog/the-log-is-the-agent) made the argument for the record itself: if a provider holds the only durable copy of your agent's history, the provider owns your agent. The same argument now climbs a level. The control plane is where the log lives and the harness runs, so you should be able to own that too.
483
484## How We Handle This in Practice at Omnara
485
486At Omnara, the control plane owns each agent’s durable log and lifecycle. Temporary workers advance the loop when messages, tool results, or other events arrive.
487
488Machines join the execution plane through a small daemon that connects outbound to the control plane. The control plane records a command, the daemon claims and executes it, and the result is written back to the agent’s log. The worker that requested the action and the machine that performs it do not need to be alive at the same time.
489
490This makes machines tools. An agent can discover, acquire, target, and release them as needed. If a machine goes offline or fails to complete an action, the outcome is written back as a structured result, and the agent can decide what to do next. A machine provides compute, files, network access, and hardware. It never provides the agent’s identity.
491
492## Conclusion
493
494The point of this piece was twofold: to pull apart the agent from its execution environment, and make the resulting architecture serverless. Agents broke the status quo. An agent that can outlive your presence shouldn't inherit the lifecycle of your device.
495
496Put the loop in a persistent control plane. Make it serverless. Expose machines as tools. The rest falls into place: availability, resilience, scaling, efficiency.
497
498That's the system we're building at Omnara. We’re preparing to open-source our control plane for agents soon. You can [join the waitlist here](https://omnara.com).
499
500> **Update:** Omnara has launched. You can use Omnara Cloud or self-host.
501`,y=`---
502title: "The Harness Doesn't Matter"
503subtitle: "Just use Pi, bro"
504seoTitle: "The Harness Doesn't Matter | Omnara"
505seoDescription: "What an AI agent harness really is, why most harnesses are equivalent, and where they can still meaningfully differ."
506date: "2026-09-01"
507author: "Kartik Sarangmath"
508thumbnail: "./assets/titleimg-thdm.jpeg"
509draft: false
510---
511
512Models are out. Harnesses are in. Cheap open source models have taken the wind out of the big AI labs, and model gateways like OpenRouter have made the cost of switching models basically zero. The value is moving up the stack into the layer that wields those models: the harness.
513
514If you already know what a harness is, you can skip to the “What is NOT a Harness?” section.
515
516## **What Is a Harness?**
517
518A raw LLM is a stateless function. It takes some input text like “what color is an orange” and returns some output text, usually something like “You’re absolutely right!”
519
520A harness is just a loop around the LLM, which allows it to be called multiple times.
521
522![A minimal harness loop repeatedly waits for user input and calls an LLM.](./assets/img1-thdm.png)
523
524There, that’s a harness! With it, we can talk to the LLM forever. The only problem is that the LLM won’t remember what you said one message ago, since it’s stateless.
525
526To solve this really challenging problem, our brightest minds came together and arrived at a brilliant solution: why not pass the entire conversation to the LLM on every call?
527
528![A harness preserves context by appending user and agent messages to the conversation passed to the LLM.](./assets/img2-thdm.png)
529
530Now, every time you talk to the LLM, it remembers what you said before.
531
532One more issue though. It can only respond with text. How do we get it to actually do things? We give it tools:
533
534![A complete harness loop maintains conversation history, calls the LLM, runs requested tools, and continues until the turn ends.](./assets/img3-thdm.png)
535
536That’s pretty much it. That’s AGI. It remembers everything you said, it can continue conversations, it can solve math problems that haven’t been solved in decades, it can create trillions of dollars in the stock market, it can reply to Dax tweets a second after they’re posted.
537
538From here, you can add MCPs, skills, memory, subagents, bash, and everything else you’ve ever seen an agent do. Every modern harness is some version of this same loop. This loop is the harness.
539
540## **What Is NOT a Harness?**
541
542An interface or UI is NOT the harness. There is no such thing as an “iMessage harness” or a “Slack harness.” The interface is whatever sends messages to the harness and displays what comes back.
543
544Harnesses like OpenCode and Codex make this distinction pretty clear because they implement a client-server architecture. [OpenCode’s server](https://
544opencode.ai/docs/server/) runs the harness loop, while its TUI connects as a client. [Codex’s app-server](https://learn.chatgpt.com/docs/app-server) serves the same role for Codex CLI, their desktop app, and interfaces such as its VS Code extension. A Slack bot interface could talk to the OpenCode server just like its TUI does, and now you’re using OpenCode through Slack. You could even use the Codex CLI as the frontend for an OpenCode server (idk why you’d want to do that, but I’m not judging).
545
546The system prompt, tools, memory system, subagents, plan mode, and pretty much every other agent “primitive” you care about are also NOT the harness. They all reduce to the same two variables the loop already takes in: the system prompt and the tool set. MCP just adds more tools, a subagent is just a tool that launches another loop, and skills, memory, and plan mode change the system prompt and add new tools. Take the toy harness in the above example, drop in Claude Code’s system prompt, reproduce its tool definitions and implementations, and you basically have the Claude Code harness (probably a bit better, actually).
547
548![The interface and configurable system prompt and tools sit above the underlying harness loop.](./assets/img4-thdm.jpeg)
549
550Being able to edit the system prompt and tools is table stakes for a harness. If you can customize those two things, you should be able to reproduce pretty much the exact behavior of another harness without changing the harness code itself. For example, give Pi the same tools and system prompt as OpenCode, and it should behave like vanilla OpenCode without changing any of Pi’s harness code. Here’s a repo that I (i.e. Codex (i.e. gpt-5.6-sol)) made that actually does that, and you can see the results of the two harnesses side by side: [harness-equivalence](https://github.com/omnara-ai/harness-equivalence).
551
552If a harness can be used to represent any other harness, what does that mean?
553
554## **The Harness Doesn’t Matter**
555
556All that matters is the system prompt and tools you give the harness, as well as the interface to interact with that harness. That’s where all the alpha is. That’s where all the value will flow. You’re welcome, go use this information to make you millions in the stock market.
557
558Of course, this argument relies on some pretty big assumptions that I’ve conveniently glossed over. What if, instead of using our brilliant append-the-entire-conversation algorithm, the harness rebuilt the conversation state at every turn? It could use another LLM to decide what gets pruned or emphasized, or route hard tasks to a super smart LLM and easier tasks to a workhorse. You couldn’t easily reproduce a harness like that by handing Pi a system prompt and some tools, because the difference would live in the harness algorithm itself.
559
560It seems like this is where harnesses could truly differentiate themselves and create the most leverage. There are so many different algorithms that could be used!
561
562But for some reason, every popular harness manages conversation state in basically the same way. They also don’t do model routing. They all behave like the simple while loop above. The existing conversation stays as is, anything new gets appended, and the same LLM gets called again.
563
564And that won’t change any time soon, because of the prompt cache.
565
566It’s much cheaper to call an LLM with text that is prefixed with the EXACT same text as the previous call because of something called the prompt cache ([here’s why it’s cheaper](https://huggingface.co/blog/not-lain/kv-caching)). The LLM provider has already done the work for that part of the conversation, so it only has to process whatever comes after it. Change one letter and everything after it gets processed from scratch and billed at full price. On Claude, uncached input costs 10x as much as reading those tokens from the cache ([Claude pricing](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing)).
567
568Since everyone is scared of paying 10x more on their already-egregious model bills, pretty much every harness goes out of its w
568ay to avoid missing the cache. The safest way to do that is to leave the existing conversation untouched and only add to the end. That’s why they all end up as the same rudimentary loop.
569
570There is, however, one point where the cache is going to break no matter what, and the harness can rebuild the conversation however it wants.
571
572## **Compaction**
573
574Compaction is arguably the biggest way that harnesses differ today, at least when it comes to performance-related features. LLMs have a limit on how much conversation you can pass into them, so when a conversation gets close to that limit, compaction summarizes or removes old parts so the LLM can keep going. The full conversation can still exist in logs, but the model only sees the compacted version.
575
576Compaction can be implemented however you want, because the prompt cache is going to break anyway. A harness can prune old tool results, or use another LLM to summarize the older conversation, or keep only the last few messages, or do something completely different. The possibilities are endless. Compaction is especially important for long-running tasks because it decides what the LLM remembers over time. Codex is commonly praised for its compaction, which is implemented via [server-side compaction](https://developers.openai.com/cookbook/examples/gpt-5/codex_prompting_guide#compaction). This returns opaque encrypted content, so you can’t inspect the compaction content, and that compaction content only works with OpenAI’s API (I’m not a fan of this lock-in, but that’s besides the point). Whatever algorithm they’re using is good, and it’s a big reason long-horizon tasks feel great on Codex.
577
578In practice, most harnesses land on the same basic algorithm. They summarize older history with an LLM, keep the recent messages, and sometimes prune tool outputs. The summary prompts and cut points differ, but that’s about it. So even compaction is not that different between harnesses. And in the worst case, you can just give the harness a tool that searches through its conversation history, which can make up for a lot of bad compaction.
579
580## **RL’ed Harnesses**
581
582Another argument from the labs is that Claude Code and Codex have an advantage because their models were trained inside those exact harnesses. But since Codex and Claude Code are both open source ([iykyk](https://x.com/trq212/status/2092305080158748741)), you can copy their system prompts and tool implementations into Pi, and the model wouldn’t know the difference. Also, if AGI gets confused because an \`edit\` tool takes \`path\` instead of \`file_path\`, we should probably sell our Nvidia stock.
583
584Public benchmarks also show that it’s a toss-up. On [Terminal-Bench 2.0](https://www.tbench.ai/leaderboard/terminal-bench/2.0), GPT-5.5 scored 84.7% with NexAU-AHE and 83.1% with Capy, both ranking above its 82.2% with Codex. The current [Terminal-Bench 3.0](https://www.frontierbench.ai/) leaderboard is topped by Opus 5 running in [mini-SWE-agent](https://github.com/swe-agent/mini-swe-agent) at 42.7%, ahead of every native model-harness pairing on the board. [Terminal-Bench 2.1](https://www.tbench.ai/leaderboard/terminal-bench/2.1) goes the other way, with the native harnesses coming out on top.
585
586The native harness wins some and loses some, and it’s pretty arbitrary when or why that happens. Training the model inside a particular harness might make the model more comfortable with those tools, but whatever advantage that creates should only shrink as models get better, if it even mattered in the first place.
587
588## **Conclusion**
589
590You don’t need to build your own harness, and you don’t need a harness for all your harnesses. I made this post because I kept seeing people on X treat the harness itself like some deep optimization problem, and I think most people would save a lot of time by just using a good one and moving on (just use Pi, bro). Worry about the system prompt and tools you give it, and how you interface with it!
591
592![A bell curve meme contrasts confusion about harnesses with the view that the harness does not matter.](./assets/img5-thdm.jpeg)
593
594_this comes from a place of love_
595
596## **One Last Thing**
597
598I’m building my own harness.
599
600![A surprised Pikachu reacts to the announcement of another harness.](./assets/img6-thdm.jpeg)
601
602_you right now_
603
604### **Why?**
605
606Because the harness matters for reasons that aren’t directly tied to task performance. I just haven’t covered those in this post.
607
608Next week I’ll tell you why the harness actually matters :)
609`,f=`---
610title: "The Log Is the Agent"
611subtitle: ""
612seoTitle: "The Log Is the Agent | Omnara"
613seoDescription: "An agent is its durable event history: the log that lets it resume, fork, migrate, and survive failures across models, runtimes, and machines."
614date: "2026-06-11"
615author: "Ishaan Sehgal"
616thumbnail: "./assets/thumbnail.png"
617---
618
619Think about a character you have spent 100 hours playing in Skyrim or Elden Ring.
620
621What exactly is your character?
622
623- Is it the game engine? No.
624- Is it the PlayStation? No.
625- Is it the controller? No.
626
627Your character is the save file.
628
629If your PlayStation bursts into flames, your character is not dead. You buy another PlayStation, download your save file from the cloud, and your character is exactly where they were, mid-swing.
630
631![The console lies broken on the left, but the glowing save file streams across to the warrior, who fights on](./assets/save-file-metaphor.png)
632
633AI agents are hitting the same inflection point.
634
635Most people think an agent is the model, the runtime, or the loop currently executing the task. Those things matter. But they aren't the agent.
636
637In this piece I'll argue that **an agent is its data**: specifically its history of events, which we'll call the log. When the log is constructed correctly, an agent can be resumed from it alone. And that single property unlocks a whole set of capabilities that make even advanced agent use cases easier to reason about and build into everyday applications.
638
639## Defining the Agent
640
641So what is the log? Defining the agent means defining the log. At its core, the log is the event history: every user input, model output, tool call, and tool result that accumulates as the agent works: an append-only record of everything that happened.
642
643The log also carries a reference to the session definition: the system prompt, tool descriptions, and skills the agent is running under. This doesn't change from turn to turn, so it's more a versioned constant than an active part of the stream.
644
645Together they form the agent's full state. Nothing the agent is lives in the runtime, the model, or the tools; they're just interpreters and appenders. They read the durable state, act on it, and write the next event back. So you can hand a fresh executor the same log and it will reconstruct exactly where the agent was and continue from there. The log is enough, on its own, to resume the agent.
646
647## The Log in Practice
648
649Once you define the agent as the log, the rest of the system gets easier to reason about. Every operation on the agent either **reads the log, appends to it, or renders a view of it**. The model reads a view and produces the next action. The tool runner executes a tool call and appends the result. The UI reads the log and renders a timeline; the tracing system reads it and renders traces;
649 an auditor reads it to reconstruct what happened. It's the same pattern databases settled on years ago: the tables, indexes, and caches are all projections over an underlying log of changes. Diagram below adapted from [@dexhorthy](https://x.com/dexhorthy)'s excellent [12-factor agents](https://github.com/humanlayer/12-factor-agents); worth a read if you haven't.
650
651![A high-level agent control loop, running on the log.](./assets/control-loop.png)
652
653_A high-level agent control loop, running on the log._
654
655In practice, a simplified loop can look like this:
656
657\`\`\`python
658while session_has_work:
659    take_temporary_lease(session_id)
660    state = reconstruct_from_log(session_id)
661    response = model.next(state)
662    append(response, to=session_log)
663
664    if response.requests_tool:
665        append(tool_call_started, to=session_log)
666        result = run_tool(response.tool_call)
667        append(result, to=session_log)
668        continue
669
670    finish_turn(session_id)
671\`\`\`
672
673It looks simple, but the insight is that each pass claims a temporary lease, reads the log, advances one step, and writes the result back. The loop is idempotent and fault-tolerant: as long as every meaningful state transition is durably written, any executor can pick up the session and carry on.
674
675## Compaction Is Not the Log
676
677While the log can grow indefinitely, a model's view of it can't: context windows are finite. You can't hand the model the entire raw history on every call. This is where compaction comes in. And since compaction replaces everything before it with a summary, doesn't that destroy the claim that the log is the agent?
678
679No, it doesn't. Remember, compaction is lossy. A compacted summary doesn't reproduce the agent's state in a smaller form; it throws information away.
680
681That actually reinforces the claim. The full log is the record; a compaction is just one projection of it, the way a materialized view isn't the database and a summary isn't the conversation. Keep the raw log and you can always generate new projections from it. Throw the raw log away and keep only the compaction, and you've lost part of the agent. So it's cleanest to treat compaction as a best-effort, lossy fork, one you resume as a new log.
682
683![The full session log streams into a smaller compacted cube, shedding detail along the way](./assets/compaction.png)
684
685## The Log Isn't the Whole World
686
687There's an obvious objection, worth taking seriously: what about tools that change state outside the log? An agent edits a file, opens a GitHub issue, sends an email. Now there's state living somewhere other than the log. Doesn't that sink the claim?
688
689No, it doesn't. You re-derive the agent's state from the log, not the world: if it already sent the email, forking back won't un-send it, and a file it edited may have changed underneath it. The log doesn't make the world deterministic or reversible. It keeps a faithful, resumable record of what the agent did and saw, which is exactly the thing you can't afford to lose. The save file makes the same move: it doesn't contain the Skyrim engine or the map, just the player-specific state needed to drop you back in. And if the world has changed since the agent last ran, the agent, much like the character, updates its worldview as it re-engages with what's around it.
690
691## Properties That Fall Out
692
693Reasoning about agents this way gives you a set of nice system properties. They aren't independent; they all fall out of the same assumption that the log is the agent.
694
695- **Reliability.** Consider what happens today with Claude Code: if the agent reaches a permission prompt and the process dies, and you resume it, the permission prompt is gone and the agent has paused. That's unacceptable in production. When the log is the agent, the executor is allowed to be fallible: a new worker picks up the session, reconstructs the state, and the permission prompt is right where it was. The process died; the agent didn't.
696- **Scalability.** Most harnesses run one process per agent, which means the agent is tied to the machine running it. When the log is the state, you flip that model: one process can advance thousands of agents, each reconstructing its state from the log on each turn. The agent isn't tied to any single machine or worker, making failover trivial. And scaling out is just adding more workers: no sticky sessions, no state migration, no coordination overhead.
697- **Forking.** Instead of one linear path, you can branch the log. One branch runs on Claude, another on GPT, another on a local Qwe
697n, each exploring a different strategy, in a different sandbox, with different tools. Exploring different approaches becomes a lot simpler to architect.
698- **Multiplayer.** Sharing an agent shouldn't mean copying a transcript into Slack. That's like sharing a screenshot of a database and calling it replication. If the log is the agent, sharing means granting access to a durable history someone else can inspect, resume, or extend.
699- **Migration.** If the agent's identity is trapped inside provider-specific assumptions, moving providers is painful. If the log is the agent, migration becomes an adapter problem. Different models may want different projections, but those are engineering problems, not identity problems. The log is the continuity, and the agent should be able to pick up and resume interchangeably with any model provider.
700
701These are only a few examples. Once you start thinking of agents this way, they become pieces of data, and many of the things you can do with data become possible with agents too.
702
703## The Log as a Second-Class Citizen
704
705Most agent harnesses today treat the log as an afterthought:
706
707- **Claude Code and Codex** write transcripts as messy JSONL files to local disk. In Claude SDK mode, those writes are fire-and-forget, meaning if anything goes wrong before the write completes, that data is gone.
708- **OpenCode** stores state in a local SQLite file; GitHub issues report corruption and data loss.
709- **Durable objects** often end up holding different shards, making reconstructing history difficult.
710
711The log is a side effect, not the system.
712
713When you treat the log as a first-class citizen, durable, structured, replayable, and independent of the machine running the agent, all of these properties become structural. You don't bolt them on. They fall out.
714
715## Owning the Log
716
717One last point I'd want you to leave with: owning the log matters enormously. If the log is the agent, then whoever owns the log owns the agent.
718
719Long-term, the strongest form of lock-in isn't model lock-in: models can be swapped. It isn't API or tool lock-in either; those can be wrapped and adapted. The deepest lock-in is log lock-in. If a provider owns the only durable record of your agent, then that provider owns your agent. You can export a projection, a transcript, say, and still lose the agent, because the agent isn't its final output; it's the path-dependent history that produced it.
720
721This is worth taking seriously right now. Anthropic has Claude Managed Agents. Google has Gemini managed agents. Every major provider is moving to own more of the stack: hosted agent loop, managed memory, sandboxes, compaction, background execution. They want to own your agents, and agents are arguably the most intimate piece of technology you'll ever run. For an agent to be useful, it needs access to your personal information, your company's data, your workflows, your decisions. The log is a record of all of that. If it lives on someone else's infrastructure, under their retention policies, queryable by their systems, then they don't just host your agent. They own it.
722
723![A glowing gold log you own stands free beside an identical log locked behind a red-lit cage](./assets/owning-the-log.png)
724
725That's not an argument against managed infrastructure; it's useful and probably inevitable for most teams. But you should know where your log lives, who can inspect it, and whether it can be replayed, forked, migrated, or exported. Once the session log is the primitive, log ownership is agent ownership.
726
727## How We Handle This in Practice at Omnara
728
729At Omnara we think about agent execution as a set of components coordinated around a durable session log. A worker advances the loop, but the worker isn't the agent; it's just the current executor.
730
731When an agent calls a tool, the worker doesn't have to sit there holding all the state in memory. It writes to the log that a tool call started, dispatches the tool to whatever execution environment it belongs in, and lets it finish elsewhere. When the tool completes, its result gets appended back to the log, and a worker, maybe a different one, reconstructs the state and continues.
732
733This matters because agent systems have to survive real-world failure: workers crash, machines restart, sandboxes vanish, tool calls run long, providers fail, users disconnect. If the agent is the running process, all of that is terrifying. If the agent is the log, it's just execution detail. The session keeps going because the agent's state was never trapped inside the thing executing it. Treating the log as the system, durable, structured, and independent of any single machine, is the core of how we've built Omnara.
734
735## Conclusion
736
737The point of this piece was to get you reasoning about agents from first principles. An agent is the durable history of the work being done, and that history is the log.
738
739Once you see it this way, a lot falls into place: reliability, scalability, compaction, forking, migration, multiplayer, ownership. It also changes how you build. You stop treating the log as exhaust the system gives off and start treating it as the system itself. The loop becomes an executor over the log. It's the same inversion databases went through years ago: the durable history is primary, and everything else is a projection.
740
741If this was useful, stay tuned to what we're building at Omnara. We're getting ready to open-source our managed agent platform, and you can [join the waitlist here](/). "The log is the agent" is just one of several pieces we think will make agents far more powerful in the future.
742
743> **Update:** Omnara has launched. You can use Omnara Cloud or self-host.
744`,w=`---
745title: "Beyond the Desk"
746subtitle: "How programming is changing, and why we built Omnara"
747seoTitle: "Beyond the Desk: Why Mobile AI Coding Agents Matter in 2026"
748seoDescription: "Learn how AI coding agents are changing software development from desk-bound workflows to mobile-first, always-on execution, and why Omnara built for that shift."
749date: "2026-01-28"
750author: "Kartik Sarangmath"
751thumbnail: "./assets/thumbnail.png"
752archiveNote: "This post covers Omnara's previous remote coding product and is retained for historical reference. Its features and usage figures refer to that product. Omnara is now an open-source managed-agent platform."
753---
754
755*The moment you step away from your desk, progress stops.*
756
757Writing software has always been a synchronous act. You sat down at a computer and translated your thoughts into code line by line, remaining fully responsible for every change as it happened. Progress depended on your presence.
758
759When AI entered programming, it didn’t immediately change this. Cursor sped up typing and answered simple questions, but the workflow itself stayed the same. Even with better autocomplete, developers were still the bottleneck, watching code change in real time and staying fully engaged throughout.
760
761The real shift came with coding agents.
762
763Claude Code made it possible to describe what you wanted to build rather than how to build it, and to let code be written in the background. By abstracting away the code view and much of the machinery of an IDE, agents encouraged a different relationship with programming, one centered on intent and direction instead of constant execution.
764
765Once code can be written without your continuous attention, the old assumptions break down. Programming no longer needs to block you, and there is no inherent reason to remain at your computer while work progresses.
766
767In practice, today’s tools still fall short. Some agents run locally and stay close to real code, but only work while you are at your computer. Others run in hosted environments, detached from the familiar codebases and setups that developers are used to working in.
768
769We built Omnara to close that gap.
770
771Omnara lets you run Claude Code on your own computer while allowing you to stay connected to it from anywhere. You can step away and continue steering from your phone without breaking the flow of work. If your machine does go offline, you can continue the session in the cloud and sync back when it returns. When typing isn’t convenient, you can speak with your agent instead.
772
773What’s stood out most to us is how naturally this fits into people’s lives. Over six thousand people have sent more than two million messages through Omnara, often away from their desks. A few of the ways we’ve heard people use it include walking the dog while building a travel app, driving to work while keeping a machine learning training job on track, or being at the gym while pushing a startup forward. The work keeps moving, even as life goes on around it.
774
775For a long time, building software meant being chained to your desk. Inspiration that struck elsewhere had no way to move forward. That’s no longer the case. We built Omnara to let you keep building when ideas show up, wherever you are.
776
777---
778
779## Try Omnara
780
781If this way of building resonates with you, try out Omnara for free.
782
783Install Omnara in your terminal:
784
785\`\`\`bash
786curl -fsSL https://omnara.com/install.sh | bash
787\`\`\`
788
789Then run Omnara in the directory you want to work from:
790
791\`\`\`bash
792omnara
793\`\`\`
794
795Join the [Discord](https://discord.gg/Dc46sYk6e3) to ask questions, share feedback, and discuss ideas with the community.
796
797— Ishaan, Kartik, and Christian
798`,b=`---
799title: "What Is an Async Agent, Really?"
800subtitle: "Spoiler: you may or may not find out"
801date: "2026-02-09"
802author: "Kartik Sarangmath"
803thumbnail: "./assets/conductor.png"
804---
805
806Look up the term "async agents" online. You'll find dozens of posts using the phrase, and the ones that bother to define the term all seem to be talking about different things. It shows up in product announcements, HN threads, and architecture blog posts, always casually, as if the meaning were obvious. So I wanted to figure out which definition was actually correct.
807
808The most common definition I saw was that an async agent is simply an agent that runs for a while. A long-running task feels async. If it keeps working while you go to the bathroom or refill your water bottle, that feels asynchronous. And it's true that a task running long enough gives you the opportunity to go do something else. But then how long does the task need to run before it counts? What about an agent that finishes in a second versus one that takes an hour? Is the fast one synchronous and the slow one async? Can I create an async agent by adding a \`sleep\` statement before running the agent? That doesn't feel like a satisfying definition.
809
810Another definition I kept running into was that async agents are the ones running in the cloud. Devin runs remotely, so it's async. Claude Code runs locally, so it's not. But if I spin up an EC2 instance, SSH in, install Claude Code, and run it there, did I just convert it into an async agent? The tool didn't change. Only the machine running it did. That can't be it.
811
812The third definition was that async agents are event-driven. The ones that get triggered without you. A PR lands and an agent spins up. A cron job fires and the agent messages you on Slack. That feels asynchronous because you aren't the one typing the command at that moment. But a cron job can kick off any program. The trigger is asynchronous, but that doesn't make the program it runs async. That's scheduling, not a property of the agent itself.
813
814None of these are wrong exactly, but none of them feel like definitions either. They're all describing properties that happen to correlate with the thing people are trying to point at. So let's go back to basics.
815
816## What's an Agent? What's Async?
817
818As Simon Willison put it, an agent is just [an LLM that runs tools in a loop](https://simonwillison.net/2025/Sep/18/agents/). I'd go a 
818step further and say an agent also needs continuity of context. If I start up two different sessions of Claude Code, those are two different agents. If I type \`/clear\` in an instance of Claude Code, I've killed one agent and spawned a new one. The context is what makes it *that* agent.
819
820Now, asynchrony.[^1] The [technical definition](https://en.wikipedia.org/wiki/Asynchrony_(computer_programming)) says it's when events happen independently of the main program flow. The key insight is that an async function isn't magically async by itself. It's async *relative to its caller*. If the caller doesn't wait, the function runs independently. If the caller waits, it's effectively synchronous. The async part isn't the function. It's the caller's decision to not block on it.
821
822[^1]: This [FastAPI post](https://fastapi.tiangolo.com/async/) is how I first really understood async programming. Still one of the best explainers out there.
823
824Think about what coding looked like before agents. You typed; work happened. You stopped typing; work stopped. You could only progress one task at a time, all of it synchronous.
825
826When coding agents arrived, they unlocked something new. They made asynchronous work possible. You could delegate work and not wait for it to finish. You could give an agent a task, go make lunch, and come back to a finished PR. For the first time, your work could keep progressing while you did something else.
827
828But that didn't make the agent inherently async. You could turn around and ask the same agent a quick question and wait for the answer. In that moment, you used it synchronously. The agent didn't change. Your behavior did.
829
830So what is this "async agent" that everyone keeps referring to? It's in the eye of the beholder. The real async agents were the friends we made along the way.
831
832But seriously, no agent is inherently async. **It just depends on whether you wait for it to finish or not.**
833
834## Using Agents Asynchronously
835
836If you *do* choose to use agents asynchronously, you unlock something powerful: concurrency. You can delegate multiple tasks to multiple agents and let them all make progress at the same time. But once you do that, you hit a practical problem: they're all working on the same codebase on your machine. That's not how real engineering teams work. Every engineer has their own computer, their own copy of the code, their own branch.
837
838Why not just have everyone edit the same codebase in real time, like Google Docs? Seems like it would eliminate an entire class of problems: broken merges, conflicting edits, diverging branches… basically all the stuff Git makes you deal with. Collaborative coding has been tried, and it hasn't caught on for good reason (this is what [Replit](https://replit.com/collaboration) did early on). The problem is that without isolation, one person's work-in-progress can break another person's working code. You need each task to live in its own environment where it can be built, tested, and validated independently.
839
840Agents run into the same problem. They need their own copy of the codebase to make progress without stepping on each other. That's why tools like [Conductor](https://www.conductor.build/) and [Omnara](https://omnara.dev) rely on git worktrees, why people like [Boris (creator of Claude Code) literally clone whole directories](https://x.com/bcherny/status/2017742743125299476), and why apps like [Codex Web](https://developers.openai.com/codex/cloud/) or [Sculptor](https://imbue.com/sculptor/) use isolated VMs or local containers. Each agent gets its own workspace so one agent's progress doesn't break another's.
841
842With isolated workspaces, you're the manager. You're the one spawning agents, watching agents, coordinating agents. Delegating work to agents is a huge force multiplier. It dramatically increases output, but it also increases the amount of context you have to manage and switch between. There is a ceiling.
843
844Which begs the question: can an agent manage the agents?
845
846## So What Should "Async Agent" Actually Mean?
847
848Honestly, we probably shouldn't use the term at all. But if you're going to anyway, it should mean something that isn't already true of every agent. And there's a natural parallel from programming that gives us exactly the right distinction.
849
850In software, there's a difference between an async *function* and the *runtime* that manages it. An async function can run without blocking its caller. The async runtime is the thing that manages the event loop: it spawns functions, schedules them, and coordinates their results. The function is just along for the ride. The runtime is doing the orchestration.
851
852Agents map onto this perfectly. Every agent today is an async function. You call it, it runs, you go do something else. But an async *agent*, in the meaningful sense, is the runtime. It's an agent that manages its own event loop of other agents.
853
854You give it a task; it spins up a subagent in a background workspace and keeps interacting with you. You ask it to build a login page. "Got it." It delegates that to another agent running in the background. You ask it for a billing page. "Sure, and by the way, the login agent wants to know if you prefer GitHub or Google OAuth." You're not switching tabs or managing ten sessions. The agent is managing the team. You're talking to one entity, and that entity orchestrates all the others.
855
856You stopped being the developer. You stopped being the manager. You're just the person with the intent, and the agent figures out the rest.
857
858Early attempts at this model exist. [Claude's Agent Teams](https://code.claude.com/docs/en/agent-teams) and [Gastown](https://github.com/steveyegge/gastown) are early examples. And Cognition's ["Don't Build Multi-Agents"](https://cognition.ai/blog/dont-build-multi-agents) post explains why it's hard today: context sharing, token costs, unreliable inter-agent reasoning. But these are engineering constraints, not architectural ones. As models get cheaper, context windows expand, and agent-to-agent communication improves, this architecture will stop being experimental and start becoming the default.
859
860In programming, we distinguish an async runtime from a regular program because it manages concurrent execution. It spawns functions, schedules them, coordinates their results, and handles the plumbing that makes this possible: event loops, callbacks, shared state. A sequential program does not.
861
862The same distinction applies to agents. An agent that spawns subagents, manages their execution, shares context between them, and coordinates their results is architecturally different from an agent that just grinds through a task on its own.
863
864So if you're going to call something an "async agent," call it *that*. Not an agent that runs for a while. Not an agent that runs in the cloud. Not an agent triggered by a cron job. An agent that manages other agents concurrently. Everything else is just an agent you happened to not wait for.
865
866> **Editor's note:** The Omnara worktree example refers to our previous remote coding product and is retained for historical reference. Omnara is now an open-source managed-agent platform.
867`,v="/assets/immutable/thumbnail-BHxkU85_.png",k="/assets/immutable/diagram1-DAeUdDW9.png",I="/assets/immutable/diagram2-Bu8y-FMN.png",T="/assets/immutable/hero-Cg_SSu7G.png",x="/assets/immutable/alive-not-running-timeline-D_YQrGtD.png",C="/assets/immutable/control-execution-planes-C6LsvYcc.png",_="/assets/immutable/machine-lifecycles-as-tools-D1kyltac.png",A="/assets/immutable/machines-are-tools-BtjheLIS.png",S="/assets/immutable/one-machine-one-lifecycle-CC9n5Zsv.png",M="/assets/immutable/quarter-open-laptop-meme-EaOVAta7.png",j="/assets/immutable/thumbnail-DupNLuTB.png",W="/assets/immutable/img1-thdm-CyqFIbHa.png",D="/assets/immutable/img2-thdm-BkWtJh7e.png",P="/assets/immutable/img3-thdm-CGCC9iaE.png",L="/assets/immutable/img4-thdm-E_l6TVyW.jpeg",O="/assets/immutable/img5-thdm-DoR0PtQB.jpeg",H="/assets/immutable/img6-thdm-CgD5cynh.jpeg",B="/assets/immutable/titleimg-thdm-DAYWxpOq.jpeg",N="/assets/immutable/compaction-bgCnVYzw.png",Y="/assets/immutable/control-loop-D23esN3u.png",Z="/assets/immutable/owning-the-log-BU5Q0-GY.png",E="/assets/immutable/save-file-metaphor-MmA_uCQk.png",G="/assets/immutable/thumbn
867ail-C2C6DJ8V.png",z="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY0MCIgdmlld0JveD0iMCAwIDEyMDAgNjQwIiBmaWxsPSJub25lIj4KICA8cmVjdCB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI2NDAiIHJ4PSIzMiIgZmlsbD0iIzBCMEIwQiIgLz4KICA8cmVjdCB4PSI4MCIgeT0iMTIwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjE2MCIgcng9IjIwIiBmaWxsPSIjMTExODI3IiBzdHJva2U9IiMxRjI5MzciIC8+CiAgPHJlY3QgeD0iNDQwIiB5PSIxMjAiIHdpZHRoPSIzMjAiIGhlaWdodD0iMTYwIiByeD0iMjAiIGZpbGw9IiMxMTE4MjciIHN0cm9rZT0iIzFGMjkzNyIgLz4KICA8cmVjdCB4PSI4MDAiIHk9IjEyMCIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIxNjAiIHJ4PSIyMCIgZmlsbD0iIzExMTgyNyIgc3Ryb2tlPSIjMUYyOTM3IiAvPgogIDxyZWN0IHg9IjI2MCIgeT0iMzYwIiB3aWR0aD0iNjgwIiBoZWlnaHQ9IjE4MCIgcng9IjI0IiBmaWxsPSIjMTExODI3IiBzdHJva2U9IiMxRjI5MzciIC8+CgogIDx0ZXh0IHg9IjE3MCIgeT0iMjEwIiBmaWxsPSIjRTJFOEYwIiBmb250LWZhbWlseT0iSW50ZXIsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMjQiPkNsaWVudDwvdGV4dD4KICA8dGV4dCB4PSI1MTUiIHk9IjIxMCIgZmlsbD0iI0UyRThGMCIgZm9udC1mYW1pbHk9IkludGVyLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjI0Ij5BUEk8L3RleHQ+CiAgPHRleHQgeD0iODgwIiB5PSIyMTAiIGZpbGw9IiNFMkU4RjAiIGZvbnQtZmFtaWx5PSJJbnRlciwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIyNCI+QWdlbnRzPC90ZXh0PgogIDx0ZXh0IHg9IjUwMCIgeT0iNDUwIiBmaWxsPSIjRTJFOEYwIiBmb250LWZhbWlseT0iSW50ZXIsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMjQiPk9tbmFyYSBQbGF0Zm9ybTwvdGV4dD4KCiAgPHBhdGggZD0iTTQwMCAyMDBINDQwIiBzdHJva2U9IiMzOEJERjgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiAvPgogIDxwYXRoIGQ9Ik03NjAgMjAwSDgwMCIgc3Ryb2tlPSIjMzhCREY4IiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgLz4KICA8cGF0aCBkPSJNNjAwIDI4MFYzNjAiIHN0cm9rZT0iI0Y1OUUwQiIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+Cjwvc3ZnPgo=",R="/assets/immutable/thumbnail-B4mCIFCl.png",U="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSIgdmlld0JveD0iMCAwIDEyMDAgNjc1IiBmaWxsPSJub25lIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYmciIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIxIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzBCMEIwQiIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiMxMTE4MjciIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzBGMTcyQSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSIgcng9IjQ4IiBmaWxsPSJ1cmwoI2JnKSIgLz4KICA8cmVjdCB4PSI4MCIgeT0iODAiIHdpZHRoPSIxMDQwIiBoZWlnaHQ9IjUxNSIgcng9IjMyIiBmaWxsPSIjMTExMTExIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS1vcGFjaXR5PSIwLjA4IiAvPgogIDxjaXJjbGUgY3g9IjE4MCIgY3k9IjE4MCIgcj0iNDgiIGZpbGw9IiNGNTlFMEIiIGZpbGwtb3BhY2l0eT0iMC4yIiAvPgogIDxjaXJjbGUgY3g9IjEwMDAiIGN5PSI1MjAiIHI9IjY0IiBmaWxsPSIjMzhCREY4IiBmaWxsLW9wYWNpdHk9IjAuMTgiIC8+CiAgPHRleHQgeD0iMTQwIiB5PSIyNjAiIGZpbGw9IiNGOEZBRkMiIGZvbnQtZmFtaWx5PSJJbnRlciwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSI1NCIgZm9udC13ZWlnaHQ9IjYwMCI+T21uYXJhIEJsb2c8L3RleHQ+CiAgPHRleHQgeD0iMTQwIiB5PSIzMzAiIGZpbGw9IiM5NEEzQjgiIGZvbnQtZmFtaWx5PSJJbnRlciwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIyNCI+VXBkYXRlcywgZ3VpZGVzLCBhbmQgcHJvZHVjdCBzdG9yaWVzPC90ZXh0Pgo8L3N2Zz4K",F="/assets/immutable/conductor-84y5f0Fy.png",q=Object.assign({"../content/blog/mobile-coding-landscape/index.md":g,"../content/blog/sandbox-sync/index.md":m,"../content/blog/serverless-agents/index.md":p,"../content/blog/the-harness-doesnt-matter/index.md":y,"../content/blog/the-log-is-the-agent/index.md":f,"../content/blog/welcome-to-omnara/index.md":w,"../content/blog/what-is-an-async-agent-really/index.md":b}),J=Object.assign({"../content/blog/mobile-coding-landscape/assets/thumbnail.png":v,"../content/blog/sandbox-sync/assets/diagram1.png":k,"../content/blog/sandbox-sync/assets/diagram2.png":I,"../content/blog/sandbox-sync/assets/hero.png":T,"../content/blog/serverless-agents/assets/alive-not-running-timeline.png":x,"../content/blog/serverless-agents/assets/control-execution-planes.png":C,"../content/blog/serverless-agents/assets/machine-lifecycles-as-tools.png":_,"../content/blog/serverless-agents/assets/machines-are-tools.png":A,"../content/blog/serverless-agents/assets/one-machine-one-lifecycle.png":S,"../content/blog/serverless-agents/assets/quarter-open-laptop-meme.png":M,"../content/blog/serverless-agents/assets/thumbnail.png":j,"../content/blog/the-harness-doesnt-matter/assets/img1-thdm.png":W,"../content/blog/the-harness-doesnt-matter/assets/img2-thdm.png":D,"../content/blog/the-harness-doesnt-matter/assets/img3-thdm.png":P,"../content/blog/the-harness-doesnt-matter/assets/img4-thdm.jpeg":L,"../content/blog/the-harness-doesnt-matter/assets/img5-thdm.jpeg":O,"../content/blog/the-harness-doesnt-matter/assets/img6-thdm.jpeg":H,"../content/blog/the-harness-doesnt-matter/assets/titleimg-thdm.jpeg":B,"../content/blog/the-log-is-the-agent/assets/compaction.png":N,"../content/blog/the-log-is-the-agent/assets/control-loop.png":Y,"../content/blog/the-log-is-the-agent/assets/owning-the-log.png":Z,"../content/blog/the-log-is-the-agent/assets/save-file-metaphor.png":E,"../content/blog/the-log-is-the-agent/assets/thumbnail.png":G,"../content/blog/welcome-to-omnara/assets/architecture.svg":z,"../content/blog/welcome-to-omnara/assets/thumbnail.png":R,"../content/blog/welcome-to-omnara/assets/thumbnail.svg":U,"../content/blog/what-is-an-async-agent-really/assets/conductor.png":F}),Q=n=>{const t=/^---\s*\n([\s\S]*?)\n---\s*\n?/.exec(n);if(!t)return{data:{},content:n};const a=t[1]??"",i=n.slice(t[0].length),e={};return a.split(`
868`).map(r=>
868r.trim()).filter(Boolean).forEach(r=>{const l=r.indexOf(":");if(l===-1)return;const o=r.slice(0,l).trim(),s=r.slice(l+1).trim().replace(/^['"]|['"]$/g,"");if(o==="draft"){e.draft=s.toLowerCase()==="true";return}s&&(o==="title"&&(e.title=s),o==="subtitle"&&(e.subtitle=s),o==="seoTitle"&&(e.seoTitle=s),o==="seoDescription"&&(e.seoDescription=s),o==="archiveNote"&&(e.archiveNote=s),o==="date"&&(e.date=s),o==="author"&&(e.author=s),o==="thumbnail"&&(e.thumbnail=s))}),{data:e,content:i.trim()}},V=n=>{if(!n)return null;const t=/^(\d{4})-(\d{2})-(\d{2})/.exec(n);if(!t)return null;const a=new Date(Date.UTC(Number(t[1]),Number(t[2])-1,Number(t[3])));return Number.isNaN(a.getTime())?null:a},$=n=>n.date?n.date.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric",timeZone:"UTC"}):n.dateLabel||"Undated",c=(n,t)=>{if(!t)return;if(/^(https?:|data:|#|\/)/.test(t))return t;const a=t.replace(/^\.\//,""),i=`${n}/${a}`;return J[i]},X=(n,t)=>{const a=/!\[([^\]]*)\]\(([^)]+)\)/g;return n.replace(a,(i,e,r)=>{const l=r.trim(),o=/^(\S+)(\s+"[\s\S]*")$/.exec(l),h=o?o[1]:l,s=o?o[2]:"",u=c(t,h)??h;return`![${e}](${u}${s})`})},K=()=>Object.entries(q).flatMap(([n,t])=>{const a=n.replace(/[\\/]+index\.md$/,""),i=a.split("/").at(-1)??a,{data:e,content:r}=Q(t),l={slug:i,title:e.title??i.replace(/-/g," "),subtitle:e.subtitle??"",seoTitle:e.seoTitle,seoDescription:e.seoDescription,archiveNote:e.archiveNote,date:V(e.date),dateLabel:e.date??"",author:e.author,thumbnailUrl:c(a,e.thumbnail),content:X(r,a)};return e.draft?[]:[l]}).sort((n,t)=>{var a,i;return(((a=t.date)==null?void 0:a.getTime())??0)-(((i=n.date)==null?void 0:i.getTime())??0)}),d=K(),ee=()=>d,te=n=>d.find(t=>t.slug===n);export{$ as formatPostDate,te as getBlogPostBySlug,ee as getBlogPosts};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.