Lobs Companion is a macOS app that runs entirely on my machine. It knows my projects. It remembers what we've worked on across sessions. It edits files alongside me. It doesn't have a server.
Why local
Cloud-hosted agents have three problems: latency, privacy, and amnesia.
Latency is real. Every message round-trips to a cloud service and back. For iterative work (reading a file, making a small edit, checking the result) that adds up quickly. A local runtime answers without a network hop.
Privacy is obvious but easy to understate. Every message I send to a cloud agent gets logged, potentially used for training, and stored on someone else's infrastructure. For work on private codebases, that's not abstract.
Amnesia is the one nobody talks about. Every cloud chat session starts fresh. The assistant has no idea what I worked on yesterday, what my codebase looks like, or what I prefer. I spend tokens re-explaining context every session. Companion fixes this.
Three memory layers
Session memory is the full transcript of the current conversation. Active, ephemeral, disappears when the window closes.
Project memory is per-repository. When Companion starts in a project, it reads AGENTS.md, README.md, and git state to build a picture of the codebase. That picture persists across sessions tied to that repo.
Permanent memory is cross-session. After each session ends, the AI extracts key facts, learnings, and preferences and writes them to persistent storage. The next time I open Companion, it already knows what we figured out last time.
The combination means I never re-explain the same thing twice. Project context loads on startup. The session builds on top of it. When it ends, the useful parts get promoted to permanent memory. After a few weeks of sessions, it starts acting less like a tool I'm configuring and more like a collaborator I've worked with for a while.
The socket bridge
Companion is a Tauri (Rust) app with a React frontend and a Node.js agent runtime. The frontend and runtime don't communicate over HTTP. They use a Unix domain socket at ~/.lobs-companion/run/agent.sock, speaking newline-delimited JSON-RPC 2.0.
Why a socket instead of stdin/stdout? Clean bidirectionality. When the runtime writes structured messages and logs to the same stdout, separating them reliably is hard. The socket gives a clean dedicated channel. The run/ directory gets chmod 0700. No other process on the machine can connect.
The message types are simple: user_message, token (streamed), tool_call, tool_result, confirm_tool (for anything needing approval), cancel, shutdown. The frontend renders each one as it arrives. Tool calls that require confirmation block in the UI until I accept or reject them.
The UI
Three panels: context (what the agent knows about the current project), conversation (the chat), tool output (what tools are doing in real time). Keyboard-navigable throughout. I shouldn't need the mouse.
The design is dark glass morphism: #0A0A0F background, frosted glass panels with 20px blur, #6366F1 indigo accent. SF Pro for text, SF Mono for code, SF Symbols for icons. Panel transitions use spring physics (damping 0.8, response 0.3), not linear easing.
It sounds like aesthetic detail but it matters. An AI collaborator I open every day should feel like a native macOS app. Glass and spring physics are what native macOS apps feel like. Linear easing and flat backgrounds are what web apps ported to Electron feel like. The difference registers within thirty seconds of use.
Bidirectional editing
Both I and the agent can edit files in the open project. Changes either of us makes are on disk immediately. There's no "export to editor" step. I edit in my editor, the agent edits via the tool system, both sides see the same files.
The mtime staleness check from @agentic/tools matters here. If I edit a file while the agent has it cached from a previous read, its next edit attempt fails with a staleness error and forces a re-read. This is the same safety mechanism Claude Code uses, and it's the reason that class of conflict doesn't cause silent corruption.
The packages underneath
Companion's agent runtime reuses five packages from @agentic/* (config, llm, memory, runner, tools) plus two new ones specific to Companion: @companion/permissions (confirmation guards for writes, deletes, and shell exec) and @companion/app-runtime (CLI and daemon mode with the socket bridge).
The @agentic/* extraction happened specifically because of Companion. I needed the same patterns in a new project and didn't want to copy code. The packages exist because building the same production-hardened infrastructure twice is the wrong answer.