본문으로 건너뛰기

Architecture — Grida's Agent System Implementation

This is the exposed contract for Grida's local agent system. The protocol itself is the agent RFC; this page records how Grida binds it into @grida/agent, Desktop, and the editor bridge.

V1 scope is deliberately narrow: local AgentHost plus BYOK model providers. grida-cloud is deferred and documented separately as a future hosted provider that must fit this contract instead of shaping it.

Source-of-truth rule. Every agent feature lands in the core (@grida/agent) and is reachable from the grida-agent CLI first; Desktop and the editor UI are thin wrappers over the same AgentHost + AgentTransport surface. A capability that exists in a host but not the CLI is a bug — it means behavior leaked out of the core into a shell. The CLI is also what keeps the core testable without Electron: the lifecycle ops (run, compact, rewind, fork) are exercised end to end over HTTP in packages/grida-ai-agent/src/cli.test.ts.

Sibling docs:

The Three Orchestrators

@grida/agent/server ──HTTP──▶ editor/lib/agent-chat ──React──▶ scaffolds/
▲ AgentHost transport/hooks/display UI panels

Host supervisor / adapter (spawns the host and wires platform capabilities)
OrchestratorWhereLifetime
AgentHost@grida/agent/server (packages/grida-ai-agent/)Agent host process
Host supervisorHost app codeHost process
Desktop chat seameditor/lib/agent-chat/Per renderer (per window)

These layers are where coordination logic belongs. Everything else is a small collaborator with one job.

Exposed Contract

Anything not listed here is internal and can move.

1. AgentHost — Lifecycle Only

import { AgentHost, type AgentHostOptions } from "@grida/agent/server";

export class AgentHost {
constructor(opts: AgentHostOptions);
start(): Promise<void>;
stop(): Promise<void>;
}

export type AgentHostOptions = {
password: string;
userDataPath: string;
httpAccess: AgentServerHttpAccess;
capabilities?: Partial<AgentServerCapabilities>;
hostname?: string;
port?: number;
};

Lifecycle is the entire public class surface. Everything else is reached over HTTP. The class does not expose sessions, providers, runtime, or storage collaborators.

2. HTTP Wire

The renderer-facing surface is gated by GRIDA-SEC-004: per-spawn Basic Auth, Referer, and Origin.

MethodPathBody / QueryResponse
POST/handshakeprotocol + capabilities
POST/agent/runAgentRunInputSSE stream of UIMessageChunk; first frame is the grida-session event (sessionId)
GET/agent/stream/:sessionIdSSE replay-from-0 + live tail; 404 if no live run
POST/agent/abort{ sessionId }{ ok: true }
GET/sessionsquery: agent? workspaceId? q? includeArchived? cursor? limit?{ items: ChatSessionRow[], nextCursor }
GET/sessions/:idChatSessionRow or 404
POST/sessionsCreateSessionOptionsChatSessionRow
PATCH/sessions/:idPatchSessionOptionsChatSessionRow
DELETE/sessions/:id{ ok: true }
GET/sessions/:id/messagesChatMessageWithParts[]
POST/sessions/:id/rewind{ fromMessageId, restore? }RewindResult (or { ok, restored, session })
POST/sessions/:id/fork{ fromMessageId, metadata? }ChatSessionRow (the new forked session)
POST/sessions/:id/compactCompactionResult
POST/secrets/has{ providerId }{ has: boolean }
POST/secrets/set{ providerId, key }{ ok: true }
POST/secrets/delete{ providerId }{ ok: true }
POST/workspaces/open{ rootPath }Workspace
GET/workspacesWorkspace[]

There is intentionally no /secrets/get, no /auth/*, and no /entitlements/* in V1.

3. Provider Contract

V1 provider resolution is BYOK-only:

  1. openrouter
  2. ai-gateway
  3. unavailable (provider_down)

AgentRunOptions.providerId accepts only ByokProviderId. The package root exports BYOK_PROVIDER_METADATA, BYOK_PROVIDER_IDS, and ByokProviderId; it does not export grida-cloud provider constants. The metadata array order drives resolver precedence and Desktop settings labels. Sandbox network hosts stay in the sandbox policy layer. A future hosted provider must return the same kind of model factory the runtime already consumes.

4. Host Client Bridge

Typed host bridges are host-owned adapters around the @grida/agent protocol and client types. The agent package supplies the HTTP wire contract; each host decides how renderer code receives an authorized client capability.

import { AgentTransport } from "@grida/agent/transport";

export type HostAgentBridge = {
client: AgentTransport.Client;
capabilities: AgentServerCapabilities;
};

Desktop's concrete bridge shape is documented in desktop/renderer-bridge. It is not an agent package export.

Package Map

PackagePathOwns
@grida/agentpackages/grida-ai-agent/AgentHost, runtime, sessions, BYOK providers, workspaces, prompts, tools, HTTP routes, and sandbox policy intent.
@grida/ai-modelspackages/grida-ai-models/Model catalog + pricing table. Imported for tier/model metadata.

Current layer shape:

packages/grida-ai-agent/src/
├── index.ts # curated neutral root surface
├── server.ts # Node/server exports
├── transport.ts # AgentTransport namespace
├── agent-host.ts # lifecycle owner
├── protocol/ # provider ids, handshake, run, wire vocabulary
├── agent/ # createAgent + prompts
├── tools/ # createToolset + run_command + tool names
├── providers/ # BYOK resolver + upstream factories
├── runtime/ # AgentRuntime, runAgent, SSE registry, message-view
├── session/ # rows, SQLite store, recorder, titler, compaction, compactor
├── skills/ # discovery, project instructions, the `skill` tool
├── auth/ # auth.json persistence for local credentials
├── http/ # routes and guards
├── workspaces/ # opened workspace registry + guarded fs
├── fs/ # fs contracts/backends
├── todos/ # todo contracts/backends
└── sandbox/ # host sandbox policy intent

Published subpaths are curated:

{
"exports": {
".": "./dist/index.js",
"./server": "./dist/server.js",
"./sandbox": "./dist/sandbox/index.js",
"./transport": "./dist/transport.js",
"./tiers": "./dist/tiers.js",
"./fs": "./dist/fs/index.js",
"./fs/backends/opfs": "./dist/fs/backends/opfs.js",
"./todos": "./dist/todos/index.js",
},
}

Test Pins

Behavioral tests pin the exposed contract. The internals may move freely as long as these stay green.

describe("public API", () => {
it("exports BYOK provider ids but no hosted provider id");
it("exports session row types and wire chunk vocabulary from the root");
it("pins server, transport, sandbox, fs, todos, and tiers subpaths");
});

describe("handshake", () => {
it("does not advertise auth or entitlements in V1");
});

describe("provider resolution", () => {
it("prefers OpenRouter BYOK over AI Gateway BYOK");
it("falls back to AI Gateway BYOK");
it("throws provider_down when no BYOK key is present");
it("validates explicit BYOK provider ids");
});

describe("HTTP perimeter", () => {
it("rejects requests without Basic Auth");
it("rejects requests with wrong Referer / Origin");
it("never exposes a secrets.get route");
});

Anti-goals

  • No hosted model gateway in V1.
  • No OAuth/auth client in V1.
  • No billing or entitlement engine in V1.
  • No general provider router.
  • No window/UX framework — hosts render; AgentHost stores and runs.
  • No plugin/extension registry — tools and capabilities are fixed by the RFC.
  • No free process.spawn — shell goes through the host command policy.

Reading Order

  1. This doc.
  2. agent/foundations.md.
  3. agent/tools.md and agent/persistency.md.
  4. tools-fundamentals.md.
  5. desktop/process-model.md.
  6. GRIDA-SEC-004.

See Also