Developers

Agent runtime

Sessions, the system prompt, permission gating, and the Mock and ACP adapters.

The agent runtime lives in the desktop main process. It manages sessions, transcripts, configuration, streaming output, permission prompts, and adapter execution while keeping all app actions behind the shared tool registry.

The runtime keeps provider-specific code out of the core app. AgentService owns sessions and gating; adapters know how to talk to a specific backend or protocol.

Session model

A session contains an id, title, cwd, optional space id, optional model, adapter id, status, timestamps, and messages. AgentStore persists session metadata and messages. On startup, AgentService.hydrate() loads stored sessions and resets any crash-left running sessions back to idle. The renderer shows sessions in AgentView: a resizable session list, transcript, composer, stop button, and pending permission cards.

Configuration

Agent config is stored by AgentConfigStore and edited in Settings:

  • adapter: mock or acp
  • acpPreset: claude, codex, or custom
  • command and args for a custom ACP executable
  • model (optional) and autoAccept

The default adapter is mock, so the UI works without external setup.

System prompt

buildSystemPrompt() composes the prompt from a static base, the live tool catalog from registry.describe(), the current session context (cwd, space name, open browser tabs), and safety text reflecting whether auto-accept is enabled. The tool list is never hardcoded — if a tool is registered, the agent prompt can include it. If a tool is removed or renamed, the prompt changes with the registry.

Tool calls and permissions

Agents are just another registry caller, but not a trusted one. AgentService.gatedCall() applies this policy:

  • tools with no privileged capabilities run directly,
  • privileged tools prompt the user unless auto-accept is on or the decision was remembered,
  • denied calls return PERMISSION_DENIED,
  • approved calls write a one-use grant into PermissionService,
  • the final call goes through ToolRegistry.call() with caller: "agent" and the real session id.
Privileged capabilities are writes-files, controls-browser, starts-process, and destructive. accesses-network is still declared and audited.

The ACP adapter

AcpAdapter runs an external Agent Client Protocol subprocess over stdio:

  1. Resolve the configured preset or custom command.
  2. Spawn the subprocess in the session cwd.
  3. Initialize ACP with protocolVersion: 1.
  4. Start the per-session MCP bridge and register the session, receiving a localhost URL and bearer token.
  5. Send session/new with that MCP server, then session/prompt with the composed prompt.
  6. Map ACP session/update notifications into meith stream chunks.

ACP permission requests are allowed at the ACP layer because meith independently gates actual tool execution through AgentService and PermissionService.

MCP bridge

McpBridgeService is a dependency-light local HTTP JSON-RPC server bound to 127.0.0.1 on an ephemeral port. Each session gets a unique bearer token mapping to a binding, and tools/callmaps the external request back into the session's gated tool call function — preserving the same permission, audit, and browser-ownership model as in-process calls.

ts
// supported MCP-style methods
initialize
notifications/initialized
notifications/cancelled
ping
tools/list
tools/call