Process model
Status: V1.x in flight. Core lives in
@grida/agent; desktop is the host adapter. The RFC contract for an agent host lives in../ai/agent/environments.md#computer.
AgentSidecar is the agent host process Electron main spawns and
supervises. It's an instance of the computer environment from the
agent RFC: one long-lived process that owns
secrets, sessions, the agent loop, and the capability surface; an
Electron shell that owns windows and the OS; and a URL-loaded renderer
that reaches the host only through a typed bridge.
Naming: the agent sidecar is AgentSidecar; the npm package is
@grida/agent; the class inside is AgentHost. The name says
what owns lifecycle and capability policy. See god class
below.
Why not Electron main
Electron main is the right home for windows, menus, dialogs, protocol handlers, and OS integration. It is not the right home for:
- A provider registry resolving "which language model do I call right now?"
- An agent loop that streams over SSE across renderer reloads.
- Atomic file I/O on documents whose lifetime exceeds any single window.
- An
auth.jsonwhose lifetime is the user, not the app launch.
Coupling that to BrowserWindow lifecycles forces unrelated UI through the same IPC and bloats the security audit surface. A separate sidecar keeps the agent alive across renderer reloads, gives review one process to audit, and leaves room for a future CLI to share the same backend.
Where it sits
┌─ Electron main ────────────────────────────────────────────────┐
│ windows, menus, dialogs, file-open, deep links, single-instance│
│ │ spawn + supervise (AgentSidecarSupervisor) │
│ ▼ │
│ ┌─ AgentSidecar — AgentHost (one class wiring the services) ─────┐ │
│ │ HTTP 127.0.0.1:<random> Basic Auth + Referer guard │ │
│ │ sessions/ providers/ workspaces/ secrets/ │ │
│ │ files/ shell/ runtime/ http/ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ ┌─ Renderer (one per doc) ────────────────────────────────┐ │
│ │ loadURL("https://grida.co/desktop/...") │ │
│ │ window.grida → preload → HTTP to AgentSidecar │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The renderer never sees the sidecar's port or password as page data. Electron main generates a per-spawn password, passes it to the sidecar over stdin, and preload fetches the connection tuple through guarded IPC into closure scope. See security for the five-layer breakdown.
What AgentHost owns
| Concern | Why |
|---|---|
auth.json | One secrets file, chmod 0o600, lifetime is the user. See storage-layout. |
| Provider registry | Resolves the V1 BYOK providers: OpenRouter, then AI Gateway, then unavailable. See grida-cloud-agent-runtime for the deferred hosted provider. |
| Agent loop | Outlives the renderer; resumable by sessionId. RFC contract: session lifecycle. |
| Document registry | docId → {path, mtime}; dedups windows on the same file. |
| Workspace registry | Persisted to workspaces.json. RFC variable expansion: tools / capability requirements. |
| Atomic file I/O | Write-to-temp + rename; centralized so dirty tracking works. |
| Recent files (canon) | Persisted; addRecentDocument is a mirror, not truth. |
What AgentHost does not own
- Windows, menus, dialogs, deep links — Electron main.
- File-association plumbing (
open-file, argv, second-instance) — Electron main. The agent server doesn't know how a path arrived; it only knows the path. - Editor state, rendering, dirty-flag UI — renderer. The agent server stores bytes; the renderer decides what "dirty" means against its own snapshot.
- Subscription billing checks and usage ingest — not shipped in V1. A future hosted provider belongs behind the provider contract, not in the local agent surface.
- The agent loop's protocol — locked tools, capability surface, session schema, AI SDK v6 chunk shape. Those are the agent RFC; the agent server implements them, it doesn't define them.
God class
AgentHost (packages/grida-ai-agent/src/agent-host.ts) is the
one class that wires the services. Lifecycle:
const host = new AgentHost({
password,
userDataPath,
httpAccess,
});
await host.start(); // spawn HTTP, open SQLite, restore registries
const port = host.port;
await host.stop();
Each collaborator is a small class in its own subdirectory; the host
holds private references and shutdown order. HTTP routes are thin wrappers that
call into collaborators. The agent business logic that once made
http/routes/agent.ts
a ~660-LOC god-file now lives behind
AgentRuntime;
the route is a thin wrapper.
Division of responsibility
AgentSidecar's job. Hold every secret at runtime. Enforce its own HTTP
perimeter — Basic Auth, Referer check against /desktop/*, Origin
allowlist — as defense-in-depth under
GRIDA-SEC-004.
Stream long-running work under a sessionId the renderer can abort.
Refuse to start if the host cannot supply its HTTP perimeter config —
failing loud beats silently degrading.
The shell's job. Run one supervisor (AgentSidecarSupervisor). Forward
Electron's userData path so auth.json lands in the right place. Keep
agent server credentials inside preload closure. Validate IPC sender frames
against EDITOR_BASE_URL + /desktop/* on every native handler — the
preload's path-scoping should make this redundant; doing it anyway is
the right kind of paranoid. See security.
The renderer's job. Use window.grida as the only path to the
agent server. Start and abort agent streams through window.grida.agent.
Surface BYOK key presence honestly and let provider-unavailable run errors
come from the agent server. See renderer-bridge.
What can change
- srt wrap (V1.x). AgentSidecar runs inside
srtat the OS boundary. The supervisor flips theRunAsNodeElectron fuse and switches fromutilityProcess.forktochild_process.spawn(process.execPath, …). See sandbox-wrap. - Transport. Loopback HTTP is fine for V1. A Unix domain socket (macOS/Linux) for OS-level access control is on the table; Windows stays on loopback.
- CLI consumer.
grida-agent serve,run, andsessionsexercise the same AgentHost/client path without Electron.
See also
- Renderer bridge — the other end of the bridge.
- Security — five-layer GRIDA-SEC-004 breakdown.
- Sandbox wrap — AgentSidecar's outer-wrap policy.
- Storage layout —
${userData}file map. - Agent system RFC / environments / computer — the abstract model this implements.
- Grida Cloud Agent Runtime — deferred hosted-provider design; not shipped in V1.
- opencode — reference architecture
for agent server split, provider registry,
auth.jsonshape, agent-as-data.