Architecture
APX is organized into three top-level layers under src/. The boundary between shared logic and
transport is strict — it’s what keeps adding a new engine, tool, or surface a one-place change.
The three layers
Section titled “The three layers”core/ — pure logic
Section titled “core/ — pure logic”No transports, no Express, no Electron, no process spawning. Imports nothing from host/ or
interfaces/. This is where the LLM loop (runAgent), prompts, engine adapters, the tool registry,
the MCP runner, memory, and voice synthesis facades live. Anything that could be used from any
surface belongs here.
host/ — long-running processes
Section titled “host/ — long-running processes”Today this is just the daemon. It owns Express routing, plugin lifecycle, scheduler ticks, runtime adapters (claude-code, codex, …), conversation files on disk, and the transcription sidecar.
interfaces/ — surfaces
Section titled “interfaces/ — surfaces”Every surface a human or external client uses to talk to APX:
cli/— theapxcommandtui/—apx code, the terminal coding assistantdesktop/— the Electron floating windowweb/— the admin panelmcp-server/— theapx-mcpbinary
Directorysrc/
Directorycore/ pure logic — no transports, no Express, no Electron
Directoryagent/
- …
Directoryengines/
- …
Directorymcp/
- …
Directorymemory/
- …
Directoryroutines/
- …
Directoryruntimes/
- …
Directoryvoice/
- …
Directoryhost/ long-running processes
Directorydaemon/
- …
Directoryinterfaces/ the surfaces
Directorycli/
- …
Directorytui/
- …
Directorydesktop/
- …
Directoryweb/
- …
Directorymcp-server/
- …
Directoryskills/
Directoryapc-context/
- …
The import rules
Section titled “The import rules”These rules are enforced as architectural invariants — a violation is treated as a real bug:
core/imports from nothing inhost/orinterfaces/.host/andinterfaces/import fromcore/, but never from each other.- The daemon is the only thing that talks to plugins. Interfaces talk to the daemon over HTTP.
- Each layer keeps its own local utils; they’re only promoted to
core/when another layer needs them.
Why it matters
Section titled “Why it matters”- Every surface — CLI, TUI, Desktop, web panel, MCP server, future clients — becomes a thin
client over
core/orhost/. - Adding a new engine, prompt, or tool happens in one place.
- Adding a new surface is one folder under
interfaces/. - Refactors stop rippling: a change to
runAgent()is felt everywhere, but no surface needs to know.
Storage model
Section titled “Storage model”The layering pairs with a strict storage split: project context (agents, curated memory, MCP hints)
is committed to your repo under .apc/, while runtime state (sessions, conversations, messages,
caches) lives in ~/.apx/ and is never committed. See Projects and
Configuration for the full layout.