Consolidated technical reference for the ADE (Agentic Development Environment) system. This document is the entry point for engineers and AI agents who need to understand the shape of the system before reading feature-specific docs. Deeper subsystem docs live under docs/features/.
ADE is a local-first development control plane that orchestrates AI-assisted software engineering across parallel worktrees. The center of the system is a per-machine ADE runtime daemon (apps/ade-cli/, started with ade serve). The daemon hosts every project on that machine through a project registry and exposes a multi-project JSON-RPC surface on a Unix socket / Windows named pipe at ~/.ade/sock/ade.sock. Desktop, the terminal ade code client, the iOS app, and SSH-attached desktop windows are all peer clients that bind to a runtime — local or remote — and invoke runtime-owned actions through that one surface.
The runtime owns everything that needs to survive a client closing: worktree-per-lane git isolation, a multi-provider AI runtime, a deterministic orchestrator for multi-step missions, a Linear-integrated CTO agent acting as a team lead, a pipeline builder for visual automations, stacked pull requests with conflict simulation, computer-use proofs, a SQLite-backed memory system, the sync host that replicates projects to other devices, and the per-machine credential store and agent registry. Nothing leaves the user's machine by default: AI work runs through user-authenticated CLIs (Claude Code, Codex), local API-key routes (OpenCode server), or local model endpoints (Ollama, LM Studio, vLLM).
ADE ships as four runtime/client packages plus the marketing site:
┌───────────────────────────────┐
│ apps/web (marketing + DL page)│
└───────────────────────────────┘
┌───────────────────────────────────────────────┐
│ apps/ade-cli (RUNTIME) │
│ ─────────────────────────────────────────────│
│ `ade serve` daemon │
│ - listens on ~/.ade/sock/ade.sock │
│ - login service (launchd / systemd / Win) │
│ - multi-project RPC + project registry │
│ - sync host (cr-sqlite over WebSocket) │
│ - credential store, agent registry │
│ - dispatches CLI runtimes: │
│ claude · codex · opencode · cursor │
│ - SQLite + cr-sqlite per project (.ade/ade.db)│
│ ─────────────────────────────────────────────│
│ Also exposes: │
│ - `ade rpc --stdio` single-session over SSH │
│ - `ade <command>` typed CLI surface │
│ - `ade code` terminal Work client (Ink+React)│
└───────────────────────────────────────────────┘
▲ ▲ ▲ ▲
│ local │ local │ WebSocket │ stdio over
│ socket │ socket │ │ SSH
│ │ │ │
┌──────────────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────────────┐
│ apps/desktop │ │ ade code TUI │ │ apps/ios │ │ apps/desktop │
│ (Electron, multi-│ │ (apps/ade-cli│ │ SwiftUI │ │ window bound to a│
│ window — one │ │ /tuiClient) │ │ controller│ │ remote runtime │
│ window/project) │ │ │ │ (never │ │ (RemoteConnection│
│ LocalRuntime- │ │ defaults to │ │ runs │ │ Pool, bootstrap- │
│ ConnectionPool │ │ machine sock │ │ agents) │ │ uploads bundled │
│ │ │ │ │ │ │ runtime binary) │
└──────────────────┘ └──────────────┘ └──────────┘ └──────────────────┘
All clients share the runtime's view of
projects, lanes, chats, processes, sync.
│
▼
┌─────────────────────────┐
│ User code: git worktrees│
│ under .ade/worktrees/ │
└─────────────────────────┘
Live runtime state is replicated between paired devices through cr-sqlite changesets carried over WebSocket; the sync host runs inside the runtime daemon, not in the desktop app. The iOS app pairs with a runtime — typically the user's primary desktop-class machine. A second desktop on the same network is also a client of that runtime, not a peer host. A desktop window can be re-pointed at a runtime on a remote machine over SSH; the binding is per-window, so the same Electron process can drive a local project in one window and an SSH-bound project in another. The remote path starts ade rpc --stdio on the remote and routes runtime actions through the same multi-project JSON-RPC surface. See features/remote-runtime/README.md.
Source code crosses machines through plain git. ADE does not own a git server.
Product positioning and workflows live in docs/PRD.md. This document is strictly technical.
apps/ade-cli/ is the runtime — the per-machine source of truth — and the ade CLI surface. It ships as one Node binary that runs in several modes.
Run modes:
- Daemon (
ade serve) — the normal mode. Boots the multi-project JSON-RPC server, hosts the per-project services on demand, and listens on~/.ade/sock/ade.sock(Windows: a named pipe under\\.\pipe\ade-<hash>, with the hash derived inapps/desktop/src/shared/adeRuntimeIpc.ts). Installable / removable as a login service withade serve --install-service/--uninstall-service(per-platform installers inapps/ade-cli/src/serviceManager/). - Single-session CLI —
ade <command>connects to the local daemon over the machine socket, dispatches one project-scoped action, and exits. With--headless, the CLI bootstraps a project's services directly from the repository instead of going through a daemon — used in CI and for one-off scripts. - SSH stdio bridge (
ade rpc --stdio) — runs a single-session JSON-RPC runtime over stdin/stdout. This is what desktop'sRemoteConnectionPoolexecs over SSH afterbootstrapRemoteRuntimehas uploaded a matchingade-<platform-arch>binary. Exits when the SSH channel closes; does not expose remote memory features. - Terminal client (
ade code) — launches the Ink + React Work chat (apps/ade-cli/src/tuiClient/). Defaults to attaching to~/.ade/sock/ade.sockand will startade serveif the socket is missing.ade --socket /path coderequires a specific socket;ade code --embeddedkeeps the legacy in-process fallback explicit.
Multi-project RPC. The daemon exposes runtime-scoped methods (projects.list/add/remove/touch, sync.*, runtime/info, machineInfo.get, runtimeEvents.subscribe/unsubscribe) directly. Project-scoped operations dispatch through ade/actions/call with a projectId. Per-project services are spun up lazily by ProjectScopeRegistry (apps/ade-cli/src/services/projects/projectScope.ts) which calls createAdeRuntime({ projectRoot, ... }) the first time a project is touched. The project registry (projectRegistry.ts) is the durable list of known projects; machineLayout.ts resolves machine-wide paths under ~/.ade/. Wire formats live in apps/ade-cli/src/multiProjectRpcServer.ts.
Runtime-side services (under apps/ade-cli/src/services/):
| Directory | Role |
|---|---|
projects/ |
Project registry, project scope (per-project runtime), machine layout. |
sync/ |
Sync host, peer client, device registry, pairing store, PIN store, sync protocol, remote command service, Tailscale CLI resolver. The sync host now lives here; desktop's old in-process host is disabled by default (env-gated ADE_ENABLE_DESKTOP_SYNC_HOST=1 for diagnostics only). |
credentials/ |
Per-machine credential store. |
agentRegistry.ts |
Per-machine agent registry. |
Service managers. apps/ade-cli/src/serviceManager/installLaunchd.ts (macOS), installSystemd.ts (Linux), installWindows.ts (Windows) register ade serve as a login-time service. index.ts is the platform router; common.ts carries shared types (ServiceManagerResult, ServiceManagerStatusResult).
Session identity. The runtime resolves caller role from ADE context env vars and command flags. Role vocabulary: cto, orchestrator, agent, external, evaluator.
Action surface. First-class command families cover lanes, git, diffs, files, PRs, path-to-merge, runs, shells, chats, agents, CTO, Linear, tests, proof, memory, settings, the iOS Simulator (ade ios-sim / ade ios / ade simulator — see features/ios-simulator/README.md), the Cursor Cloud bridge (ade cursor cloud agents | runs | artifacts | repos | models | me — talks directly to @cursor/sdk without going through the ADE socket), the App Control bridge for Electron apps (ade app-control / ade app / ade electron — launch, connect, stop, status, screenshot, snapshot, inspect, select, click, type, scroll, key, targets, attach, logs, terminal write, terminal signal — see features/computer-use/app-control.md), the chat-scoped terminal (ade terminal list / read / write / signal / active), and a generic ade actions run <domain.action> escape hatch for every registered ADE service action. The action allow-list adds two domains for these surfaces: app_control (every public method on AppControlService) and terminal (list, read, write, signal, activeForChat against ptyService).
Proof subcommands — ade proof capture (alias of screenshot), ade proof attach <path>, ade proof record, ade proof launch, ade proof interact, ade proof list/status/environment/ingest. attach infers the artifact kind from the file extension and routes through ingest_computer_use_artifacts with backendStyle: "manual". Capture-style commands set preferHeadless: true on the plan so the connection layer drops to headless mode unless --socket is explicitly requested. All proof subcommands accept --owner-kind / --owner-id (with chat and pr aliases) to layer an explicit owner on top of the inferred session identity.
Bundled runtime artifacts. Per-platform ade-<platform-arch> binaries plus their native dep tarballs live under apps/desktop/resources/runtime/. release-core.yml builds the cross-platform set; bootstrapRemoteRuntime uploads them on first SSH connect from the desktop client.
Headless install. A standalone runtime can be installed on a headless machine without going through the desktop installer:
curl -fsSL https://github.com/arul28/ADE/releases/latest/download/install.sh | shUse ADE_VERSION=vX.Y.Z for a pinned release or ADE_INSTALL_DIR to choose the destination directory.
Install + PATH wiring (when the desktop ships ade). On macOS / Linux the desktop installer drops the launcher at $HOME/.local/bin/ade; on Windows it lands at %LOCALAPPDATA%\ADE\bin\ade.cmd. After a successful install on Windows, the packaged .cmd installer adds the target directory to HKCU Environment\Path when needed and broadcasts an environment-change notification. After a successful install on POSIX, ensureUserBinOnShellPath appends a marked export PATH="$HOME/.local/bin:$PATH" block to the user's shell rc (.zshrc for zsh, .bashrc for bash, .profile otherwise) iff (a) the install dir isn't already on the inherited PATH and (b) the file doesn't already contain the marker / line / target dir. The install IPC reply tells the renderer which profile was edited so the Settings/Onboarding UI can prompt the user to open a new terminal or source it.
Windows packaging. The installer lays down ade-cli-windows-wrapper.cmd plus an ade-cli-install-path.cmd helper alongside the bundled Electron Node runtime. The helper installs %LOCALAPPDATA%\ADE\bin\ade.cmd, updates the user PATH when needed, and then ade works from a new normal Windows shell without a global Node install. See §14.4 for the packaging flow.
The desktop app is a client of the runtime. It owns a trusted main process, a narrow typed preload bridge, the React renderer, and the shared TypeScript contracts that the whole monorepo (including the ADE CLI runtime) consumes — but the data plane it operates on lives in the runtime daemon.
| Directory | Role |
|---|---|
apps/desktop/src/main/ |
Node process with full OS access. Hosts windows, registers IPC handlers, routes runtime-backed APIs through local/remote runtime pools, spawns the local runtime daemon when needed, and runs the legacy in-process services that have not yet been migrated to the runtime. Entry: main.ts. |
apps/desktop/src/preload/ |
Typed bridge. Entry: preload.ts. Uses contextBridge.exposeInMainWorld("ade", { ... }). Runtime-backed APIs route through LocalRuntimeConnectionPool (local) or RemoteConnectionPool (SSH-bound window); when ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1, local-bound windows skip the daemon/event pump and use guarded in-process IPC fallbacks. |
apps/desktop/src/renderer/ |
React 18 SPA. No Node access, no filesystem access, no direct process/network. Everything goes through window.ade. Entry: main.tsx. |
apps/desktop/src/shared/ |
Types, IPC channel constants (ipc.ts), model registry (modelRegistry.ts), keybindings, and other DTOs. Imported by both desktop and apps/ade-cli. New runtime-facing types live in shared/types/remoteRuntime.ts and shared/types/core.ts. |
apps/desktop/src/generated/ |
Build-time generated code (e.g., bootstrap SQL snapshots). |
apps/desktop/src/test/ |
Shared vitest setup and fixtures. |
apps/desktop/src/types/ |
Ambient type declarations. |
Multi-window shell. main.ts hosts multiple BrowserWindow instances; opening another project opens it in a dedicated window. Each window has its own runtime binding (local pool or a specific remote target). External controllers — for example a ade code TUI — can drive desktop window navigation via the app/navigate JSON-RPC method against the runtime; the desktop's IPC tracing carries window ID so logs distinguish which renderer surface invoked a channel.
Runtime binding pools.
apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts— desktop-side client for the localade servedaemon. Spawns or attaches to the machine socket, registers local projects withprojects.add, dispatches local runtime actions, and best-effort installs the background service in packaged builds.ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1is a development/diagnostic escape hatch: preload does not pump local runtime events or issue local runtime actions, and main-process sync IPC returns a standalone unavailable snapshot or no-ops lane-presence updates instead of spawning the daemon.apps/desktop/src/main/services/remoteRuntime/— SSH-bound runtime pool.remoteTargetRegistry.tsstores saved machines under~/.ade/secrets/remote-machines.json;sshTransport.tshandles ssh-agent / key based transport;remoteBootstrap.tsdoes first-connect runtime upload + version negotiation against the bundledade-<platform-arch>binary;remoteConnectionPool.tskeeps the per-window remote runtime binding alive with reconnect / eviction;runtimeRpcClient.tsis the JSON-RPC client;runtimeDiscovery.tsdiscovers reachable runtimes on the network.
Build outputs (configured in apps/desktop/tsup.config.ts):
| Entry | Source | Purpose |
|---|---|---|
main/main.cjs |
src/main/main.ts |
Electron main process |
main/packagedRuntimeSmoke.cjs |
src/main/packagedRuntimeSmoke.ts |
Post-package smoke test for PTY spawn, Claude SDK init, Codex availability, and ADE CLI readiness. |
preload/preload.cjs |
src/preload/preload.ts |
Renderer bridge. |
Terminal-native Work chat client (Ink + React) for agents and power users who live in a shell, built into apps/ade-cli/src/tuiClient/. It is a peer of the desktop client, not a wrapper around it: it speaks the same multi-project JSON-RPC surface and binds to a runtime daemon the same way.
- Attached mode (default): connects to
~/.ade/sock/ade.sock, or to an explicit socket passed on the parentadeinvocation. Startsade serveif the socket is missing. - Embedded mode:
--embedded/--headlessruns the sharedapps/ade-cliservices in-process without going through a daemon. Used when no daemon is reachable.
Shared chat DTOs are imported from apps/desktop/src/shared/types/* (never the renderer barrel) so npm run typecheck in apps/ade-cli covers both typed commands and the TUI. Entry: apps/ade-cli/src/tuiClient/cli.tsx → apps/ade-cli/dist/tuiClient/cli.mjs, loaded by ade code. The TUI can hand off to a desktop window via the app/navigate JSON-RPC method when a desktop client is attached to the same runtime.
Native SwiftUI app acting as a controller. It pairs with a runtime daemon over WebSocket and reads live state from a local cr-sqlite-backed SQLite database that mirrors the project's ade.db. The phone never runs agents.
- Stack: native SwiftUI +
SQLite3C API + iOS system SQLite. - CRDT: pure-SQL CRR emulation layer (trigger-based change tracking) since iOS blocks
sqlite3_load_extension()/sqlite3_auto_extension(). Changesets are wire-compatible with desktop cr-sqlite. - Core services:
Database.swift,SyncService.swift,KeychainService.swift,LiveActivityCoordinator.swift. - Shipped tabs: Lanes, Files, Work, PRs, CTO, Settings.
- Shipped: APNs push pipeline (runtime-side
apnsService+notificationEventBus→ iOSAppDelegate+NotificationCategories+ Notification Service Extension), workspace Live Activity (Lock Screen + Dynamic Island), Home Screen / Lock Screen / Control Center widgets. - Planned: Missions, Automations, Graph, History tabs; iPad layout; Spotlight.
- Target: iOS 26+, iPhone + iPad.
A Vite/React SPA that serves the public marketing site and download page. Four pages: HomePage, DownloadPage, PrivacyPage, TermsPage. Independent package (ade-web), deployed via Vercel (apps/web/vercel.json). Not a runtime dependency of the desktop app. Shared-origin with the Mintlify docs site (docs.json at repo root).
ADE uses Node's native node:sqlite driver (no better-sqlite3 dependency) with a vendored cr-sqlite loadable extension:
- Engine source:
apps/desktop/src/main/services/state/kvDb.ts(schema bootstrap, CRR enablement, sync API) andcrsqliteExtension.ts(extension loader). Both the desktop main process and the ADE CLI runtime import the same engine module from here; they do not maintain parallel schemas. The database is owned by whichever process opened it first for a given project — in normal operation that is the runtime daemon, with desktop's in-process services acting as legacy fallbacks. - Database file:
<project_root>/.ade/ade.db. - WAL mode handles durability;
flushNow()is a no-op. - CRRs: eligible tables are marked via
SELECT crsql_as_crr('table_name')at startup. Virtual/internal tables (sqlite_%,crsql_%,unified_memories_fts%) are excluded. Marking is dynamic — new tables are picked up automatically unless excluded. - Sync API (
AdeDb.sync):getSiteId(),getDbVersion(),exportChangesSince(version),applyChanges(changes). Used by the sync transport. - Merge semantics: last-writer-wins per column with Lamport timestamps; each device has a site ID at
.ade/secrets/sync-site-id. - Engineering rule under CRR retrofit: app-level
ON CONFLICT(...)upserts must target PK only; secondary UNIQUE constraints do not survive CRR marking.
Schema bootstrap in kvDb.ts creates ~103 tables. Anchor tables for agents reading this doc:
| Table | Purpose |
|---|---|
projects |
One row per opened repo. Keyed by root_path. |
lanes |
Worktree-backed units of work. Types: primary, worktree, attached. Supports parent/child stacks, mission binding, color/icon/tags. |
terminal_sessions |
Tracked PTY sessions per lane with transcript path and head SHAs. The chat_session_id column (indexed) marks terminals owned by a chat (chat terminal drawer, App Control launch terminal); ptyService exposes them through the ade.terminal.* IPC and the terminal ADE action domain. |
session_deltas |
Post-session diff stats + touched files + failure lines. Input to pack generation. |
operations |
Audit log of every significant mutation (git, pack updates). Pre/post HEAD SHAs enable undo. |
process_definitions / process_runtime / process_runs |
Managed-process lifecycle (derived from ade.yaml). |
test_suites / test_runs |
Declared test suites and their execution history. |
missions / mission_runs / mission_steps / mission_step_attempts |
Mission lifecycle with runs, steps, attempts. |
pull_requests / pr_review_threads / pr_checks |
GitHub PR projections with queue and stack metadata. |
integration_proposals |
PR merge-plan simulations. Stores source lanes, pairwise results, sequential resolution state, optional adopted merge target (preferred_integration_lane_id), and merge-target drift snapshot (merge_into_head_sha). |
unified_memories + unified_memories_fts + unified_memory_embeddings |
Primary memory store + FTS4 index + vector embeddings. |
memory_procedure_*, memory_skill_index, knowledge_capture_ledger |
Procedural memories and ingestion dedupe. |
cto_core_memory_state |
Per-project CTO core-memory blob. |
computer_use_artifacts + computer_use_artifact_links |
Canonical proof-artifact records and cross-domain ownership. |
devices + sync_cluster_state |
Device registry and singleton host-authority row (host is brain_device_id internally; legacy naming). |
kv |
Generic key-value store for UI layout, config trust hashes, misc settings, and short-lived recovery records such as agent-chat-parallel-launch:<projectRoot>:<laneId>. |
Types for these tables are split into domain modules under apps/desktop/src/shared/types/. The barrel index.ts re-exports core, models, git, lanes, conflicts, prs, files, sessions, chat, missions, orchestrator, config, automations, packs, budget, usage, and more. Feature docs under docs/features/ call out the table subsets that are load-bearing for each surface.
<project-root>/
├── .ade/
│ ├── .gitignore # Tracked; ignores machine-local ADE state
│ ├── ade.yaml # Shared (tracked): processes, stacks, tests, templates
│ ├── local.yaml # Personal overrides (ignored)
│ ├── local.secret.yaml # Secret integration config (ignored)
│ ├── ade.db # SQLite + cr-sqlite (runtime, ignored)
│ ├── worktrees/<slug>-<uuid>/ # Lane worktrees (ignored)
│ ├── transcripts/ # PTY transcripts (ignored)
│ ├── cache/ # Runtime scratch (ignored)
│ ├── artifacts/ # Pack exports, history artifacts (ignored)
│ ├── memory/ # Promoted-memory markdown mirror (ignored)
│ ├── cto/
│ │ ├── identity.yaml # Shared CTO identity (tracked)
│ │ ├── core-memory.json # Runtime CTO core memory (ignored)
│ │ ├── CURRENT.md # Running status markdown (ignored)
│ │ ├── MEMORY.md # Runtime memory mirror (ignored)
│ │ └── daily/<YYYY-MM-DD>.md
│ ├── agents/<slug>/ # Per-worker identity + core memory (runtime, ignored)
│ ├── templates/ # Lane/mission templates (tracked when human-authored)
│ ├── skills/ # Exported skill markdown (tracked when human-authored)
│ ├── workflows/linear/ # Linear workflow config (tracked when present)
│ ├── project-icons/ # Imported project icon overrides (tracked when ade.yaml.iconPath points at one)
│ ├── ade.sock # Unix socket for ADE RPC (runtime)
│ └── secrets/ # Machine-local secret material (ignored)
│ ├── github/*.bin # safeStorage-encrypted tokens
│ ├── sync-site-id
│ ├── sync-device-id
│ └── sync-bootstrap-token
└── ~/.ade/ # Global state (user profile directory)
├── global-state.json # Recent projects list
└── logs/ # Main-process structured logs
Portability buckets (intentionally distinct):
- Git-tracked shared scaffold —
.ade/.gitignore,ade.yaml,cto/identity.yaml, human-authoredtemplates/**,skills/**,workflows/linear/**,project-icons/**. This is the only.ade/subset that flows through normal clone/pull. The shared.ade/.gitignoreis now*with explicit allowlist entries for those scaffold files (so the next time someone touches.ade/from a fresh tool the runtime state stays out of git automatically). - ADE sync state — the replicated
ade.dbtables that flow through cr-sqlite over WebSocket when devices join the same host. - Machine-local runtime — worktrees, caches, transcripts, artifacts, secrets, sockets, generated context/memory markdown. Never leaves the device.
Project scaffold modes. initializeOrRepairAdeProject(projectRoot, { mode }) controls whether a project gets the full shared scaffold or stays local-only:
mode: "shared"always materializes the canonical files (.ade/.gitignore,ade.yaml,cto/identity.yaml, the tracked placeholder.gitkeeps) and scrubs any leftover.ade/ignore lines from.gitignore/.git/info/exclude. Triggered automatically fromcreateLocalProject, every shared-config save, and any helper that callsensureSharedAdeProjectScaffold(projectRoot)(e.g.setProjectIconOverrideFromSelection,linearWorkflowFileService.save).mode: "auto"(the default foropenProject) keeps the project local-only when no shared scaffold files exist yet — it ensures.git/info/excludehas a.ade/entry so a brand-new clone or a personal-only setup never accidentally promotes runtime state into git, and only flips to the shared layout when shared scaffold files are already present (or after a save call promotes them).mode: "local"is reserved for force-local repair flows.
- Schema is defined idempotently —
CREATE TABLE IF NOT EXISTS+CREATE INDEX IF NOT EXISTS. - One-time schema-compat migration at startup: retrofits
NOT NULLon PKs and strips UNIQUE/FK constraints incompatible with cr-sqlite CRRs. A pre-cr-sqlite backup (<db>.pre-crsqlite-w1.bak) is written on first CRR enablement. - Feature migrations add columns via
ALTER TABLE ADD COLUMN, wrapped bycrsql_begin_alter/crsql_commit_alterto stay CRR-safe. - Targeted per-domain migrations live alongside their domain tests:
kvDb.missionsMigration.test.ts,kvDb.orchestratorMigration.test.ts,kvDb.workerAgentsMigration.test.ts. - The canonical iOS bootstrap schema is exported from desktop
kvDb.tstoapps/ios/ADE/Resources/DatabaseBootstrap.sqlso iOS stays schema-compatible.
Service entry points live under apps/desktop/src/main/services/ai/. The subsystem has four parts: provider-routed execution, permission profiles, ADE CLI-backed tool surfaces, and a deterministic orchestrator on top of those.
- Router —
aiIntegrationService.tsresolves a task → model → provider class and dispatches. - Model registry —
apps/desktop/src/shared/modelRegistry.tsis the single source of truth. EachModelDescriptorcarries identity (id,shortId,providerRoute,providerModelId), capabilities, pricing, context sizing, auth type (cli-subscription,api-key,openrouter,local), and optionalharnessProfile/discoverySourcefor safety metadata. - Classes:
- CLI-wrapped (Claude via
@anthropic-ai/claude-agent-sdk, Codex CLI via@openai/codex-sdk) — spawned as subprocesses; Claude uses the SDKquery()stream with ADE's async input pump and bundled Claude Code binary, while Codex uses its app-server JSON-RPC bridge. Authentication inherits from the user's own CLI login. ADE context is exposed through environment variables, and agents can call back into ADE with theadeCLI. - API-key / OpenRouter (Anthropic, OpenAI, Google, Mistral, DeepSeek, xAI, Groq, Together AI, OpenRouter) — routed through the OpenCode server (
opencodebinary, user-installed or bundled). Discovery viaopenCodeInventory.ts; replaces dynamic portion of the registry. - Local (Ollama, LM Studio, vLLM) — OpenAI-compatible local endpoints through OpenCode. Discovery via
localModelDiscovery.ts.
- CLI-wrapped (Claude via
- Detection pipeline:
authDetector.ts— detects subscriptions, API keys, OpenRouter, local endpoints.providerCredentialSources.ts— reads Claude OAuth credentials, Codex tokens, macOS Keychain.providerConnectionStatus.ts— builds theAiProviderConnectionssnapshot surfaced to the renderer.providerRuntimeHealth.ts— per-provider health (ready,auth-failed,runtime-failed).claudeRuntimeProbe.ts— lightweight SDK probe on force-refresh to distinguish bundled Claude binary readiness from authentication readiness.modelsDevService.ts— non-blocking 6-hour refresh that enriches pricing and context-window metadata in the registry frommodels.dev.
- ADE action status surface:
ai.getStatus,ai.listApiKeys, andai.getOpenCodeRuntimeDiagnosticsexpose the same provider readiness, stored-key, and OpenCode runtime health data to renderer settings andade codemodel setup through the shared ADE action registry. - Fallback: if no usable provider is present, ADE runs in guest mode — deterministic features (packs, diffs, conflicts) continue; AI surfaces are disabled with explanatory UI.
Permission configuration is class-based, not provider-bucketed:
permissionConfig.cli— for CLI-wrapped models. Claude usesclaudePermissionMode(default,auto,acceptEdits,bypassPermissions,plan); Codex usesapprovalMode(untrusted,on-request,on-failure,never) +sandboxPermissions(read-only,workspace-write,danger-full-access).permissionConfig.inProcess— for API/local models. ADE-defined planning/coding tool profiles constitute the full tool surface.- ADE-owned tools (repo mutation, mission control, context export) always enforce ADE's own permission and policy layers regardless of provider mode — preserving the audit boundary.
- Sandbox budgets:
maxBudgetUsdper-session cap for Claude; per-task daily budgets for narratives/PR descriptions/terminal summaries/mission planning/orchestrator.
Agent tools are split by domain:
| File | Domain |
|---|---|
ai/tools/universalTools.ts |
Turn-level memory guard + mutating tools (bash, writeFile, editFile); gates on TurnMemoryPolicyState for required turns. |
ai/tools/memoryTools.ts |
memoryAdd, memorySearch; resolveAgentMemoryWritePolicy(); MemoryWriteEvent emission. |
ai/tools/workflowTools.ts |
Mission / workflow interaction tools. |
ai/tools/ctoOperatorTools.ts |
CTO-only operator tools. |
ai/tools/linearTools.ts |
Linear integration tool surface. |
ai/tools/webFetch.ts / webSearch.ts |
Outbound web access. |
ai/tools/readFileRange.ts / globSearch.ts / grepSearch.ts |
Read-only file tools shared across all roles. |
ai/tools/editFile.ts |
Edit-path tool wired to ADE-controlled write flow. |
ai/tools/systemPrompt.ts |
Base system prompt; memory usage instructions baked in. |
ADE CLI is the cross-process action surface. Workers spawned as CLI children inherit ADE context env vars and can call the ade command to invoke ADE-owned actions layered on top of their native provider tools.
Turn classification (universalTools.ts): the chat service classifies each user turn as required (mentions fix/debug/implement/refactor → memory search mandatory before mutations), soft (explain/review/design → memory auto-injected but not gated), or none (meta/greeting → no injection/gating).
apps/desktop/src/shared/modelRegistry.ts + apps/desktop/src/shared/modelProfiles.ts:
MODEL_REGISTRY— static CLI-wrapped entries + dynamically populated API-key/local entries. Includes the Claude Opus 4.7 1M-context entry (anthropic/claude-opus-4-7-1m, aliasesopus[1m]/claude-opus-4-7[1m], 1,000,000 context / 128,000 max output,costTier: "very_high", fulllow|medium|high|maxreasoning tiers).ModelDescriptor.serviceTiers?: string[]advertises optional service tiers (today:"fast", set on the Codex CLI GPT 5.4 / 5.5 entries) that the UI's Codex Fast Mode toggle and the Codex JSON-RPCserviceTierargument key off.ModelProviderGroup="claude" | "codex" | "opencode" | "cursor" | "droid". Cursor and Droid each have their own top-level provider group used by the model picker, identity routing, and tracked CLI provider catalog.- Helpers:
getModelById,getModelPricing,updateModelPricingInRegistry,replaceDynamicOpenCodeModelDescriptors,resolveProviderGroupForModel,resolveModelDescriptorForProvider,getRuntimeModelRefForDescriptor,modelSupportsServiceTier(descriptor, tier)/modelSupportsFastMode(descriptor). - Reasoning tier passthrough (
providerOptions.ts) maps tier strings directly to each provider's native config (thinking.type,reasoningEffort,thinkingConfig.thinkingLevel, etc.) — no arbitrary token budgets. The Claude vocabulary islow | medium | high | max. - Model profiles (
modelProfiles.ts) derive the Missions UI model catalog and per-call-type intelligence defaults fromMODEL_REGISTRYrather than maintaining parallel lists.
apps/desktop/src/main/services/orchestrator/ contains the deterministic state machine that tracks mission runs, steps, attempts, and claims:
aiOrchestratorService.ts— orchestrator API.coordinatorAgent.ts+coordinatorTools.ts+coordinatorSession.ts— the intelligent coordinator that makes strategic decisions (spawn, replan, validation routing, lane transfer, escalation).orchestrationRuntime.ts,missionLifecycle.ts,missionStateDoc.ts,executionPolicy.ts,phaseEngine.ts— runtime invariants and state.adaptiveRuntime.ts,metaReasoner.ts— smart fan-out and adaptive behavior.modelConfigResolver.ts— strict phase-authoritative model resolution (explicit override → current phase model; no role-level fallback).delegationContracts.ts,missionBudgetService.ts— contract + budget enforcement.- Validation baseline: required validation contracts are runtime-enforced. Auto-spawned validator steps per target step. Missing validation blocks phase transitions with signals
validation_contract_unfulfilled,validation_self_check_reminder,validation_auto_spawned,validation_gate_blocked.
Interactive chat (Terminals, Work) bypasses mission runtime semantics but still flows through the unified executor with the same memory/permission plumbing.
Related feature docs: Chat, Agents, and Missions orchestration.
apps/desktop/src/preload/preload.ts (~2,590 lines) exposes ~550 methods on window.ade:
contextBridge.exposeInMainWorld("ade", { ... })— the only cross-isolated-world surface.- Methods are typed via TypeScript imports from
apps/desktop/src/shared/types/. - Two categories: invoke methods (
ipcRenderer.invoke(channel, args)returningPromise<T>) and event subscriptions (ipcRenderer.on(channel, handler)). contextIsolation: true,nodeIntegration: false,sandbox: false(required for preload functionality).- Global window type:
apps/desktop/src/preload/global.d.ts. window.ade.project.getDroppedPath(file)wraps Electron'swebUtils.getPathForFile()so renderer drag-drop handlers can resolve the absolute path of aFilepayload without the renderer needing Node APIs. Used by the Command Palette project browser to accept dropped folders.
apps/desktop/src/shared/ipc.ts defines the single IPC const with ~550 named channel strings in a ade.<domain>.<action> namespace:
ade.app.* # app lifecycle, clipboard text and image (writeClipboardText, writeClipboardImage), paths, image data-URL preview (getImageDataUrl)
ade.project.* # project open/close/switch/state, in-app directory browser (browseDirectories, getDetail), favicon resolver (resolveIcon)
ade.onboarding.*
ade.lanes.* # lane list/create/delete/stack/template/env/port/proxy/rebase
# delete pipeline: ade.lanes.delete + ade.lanes.delete.cancel
# + ade.lanes.delete.risk preflight + ade.lanes.delete.event push
ade.files.* # file tree, read, write, search, watch
ade.diff.* # lane-scoped change list + per-file diff / patch (diffService)
ade.pty.* # PTY spawn/write/kill, data/exit events
ade.git.* # stage/commit/push/sync/revert/cherry-pick/stash
ade.github.* # PR list, review, merge, checks
ade.prs.* # stacked PR queue, integration, issue inventory,
# Path-to-Merge orchestrator (ade.prs.pathToMerge.start /
# ade.prs.pathToMerge.stop) and ade.prs.retargetBase used
# by the queue Automate Merging modal
ade.conflicts.* # risk matrix, simulation, proposals
ade.memory.* # memory CRUD, search, health, embeddings
ade.missions.* / ade.orchestrator.*
ade.cto.* # identity, core memory, agent roster, Linear
ade.sessions.* # terminal session CRUD
ade.agentChat.* # agent chat sessions, model inventory, parallel launch state
ade.ai.cursorCloud.* # Cursor background-agents bridge: listRepositories, listAgents, listRuns, getAgent, createRun, followUp, streamRun, cancelRun, archiveAgent / unarchiveAgent / deleteAgent, listArtifacts / downloadArtifact, openChat (mirror an existing cloud agent into an ADE chat session)
ade.automations.*
ade.processes.* / ade.tests.* # processes also expose group bulk ops:
# ade.processes.startGroup / stopGroup / restartGroup
ade.config.* # project config get/save/trust
ade.keybindings.*
ade.sync.* # device registry, PIN pairing (getPin/setPin/clearPin), QR payload, lane presence announce (setActiveLanePresence), host transfer
ade.usage.* # token/cost accounting
ade.layout.* / ade.graph.*
ade.computerUse.*
ade.iosSimulator.* # macOS-only iOS Simulator drawer + Preview Lab: getStatus/launch/shutdown/screenshot/getScreenSnapshot/getInspectorSnapshot/inspectPoint/getPreviewCapability/listPreviewTargets/renderPreview/openPreviewWorkspace/startStream/stopStream/getStreamStatus/getWindowState/listWindowSources/tap/typeText/drag/swipe/selectPoint, plus the ade.iosSimulator.event push channel
ade.appControl.* # Electron app control bridge over Chrome DevTools Protocol: getStatus/launch/launchInTerminal/connect/stop/screenshot/getSnapshot/inspectPoint/selectPoint/click/typeText/scroll/dispatchKey/listTargets/attachToTarget, plus the ade.appControl.event push channel (session-started/updated/stopped, selection, screencast frame)
ade.builtInBrowser.* # in-app web browser owned by `builtInBrowserService`: getStatus/showPanel/setBounds/attachWebview/navigate/createTab/switchTab/closeTab/reload/goBack/goForward/stop/startInspect/stopInspect/captureScreenshot/selectPoint/selectCurrent/clearSelection, plus the ade.builtInBrowser.event push channel (status / open-request / selection / selection-cleared / error). Backs the Work sidebar's Browser tab and the renderer-wide `openUrlInAdeBrowser()` link router.
ade.terminal.* # chat-owned terminal control: list/read/write/signal/activeForChat. Resolves a chat's active terminal via chatSessionId so in-chat agents and the App Control panel can drive the visible launch terminal.
ade.macosVm.* # lane-tied macOS VM lifecycle and GUI control: getStatus/provision/start/stop/delete/getAgentGuide/getSharePolicy/focusWindow/captureScreenshot/selectPoint/click/typeText, plus the ade.macosVm.event push channel. Uses Lume first, direct/headless VNC when ADE has managed credentials, and sanitized mirrors for lane roots that contain ADE local state.
ade.updates.*
apps/desktop/src/main/services/ipc/registerIpc.ts (~6,400 lines) is the single registration point:
ipcMain.handle(IPC.channelName, async (event, args) => { ... })for invoke channels.- Every handler is wrapped with a 30-second timeout — if it does not resolve, the call rejects with a timeout error rather than hanging the renderer.
- Every handler emits structured tracing:
ipc.invoke.begin,ipc.invoke.done,ipc.invoke.failedwith call ID, channel, window ID, duration, and summarized args/results. AppContextindirection: handlers close over a context pointer that swaps atomically on project switch, so IPC channels remain registered across project transitions.- Multi-window shell — the app can host multiple
BrowserWindowinstances (for example when opening another project in a dedicated window). Handler tracing already carries window ID so logs and diagnostics distinguish which renderer surface invoked a channel;main.tsties each window to its project context before routing into services.
High-frequency events flow from main → renderer via webContents.send(channel, payload). Partial list:
| Event | Producer | Consumer |
|---|---|---|
ade.pty.data / ade.pty.exit |
ptyService | TerminalView, Work tab |
ade.files.change |
fileWatcherService | Files tree, diff views |
ade.processes.event |
processService | Run tab, stack buttons |
ade.tests.event |
testService | Test panel |
ade.conflicts.event |
conflictService | Conflicts page, Graph overlay |
ade.prs.event |
prPollingService | PRs page, stacked queue |
ade.missions.event / ade.orchestrator.event |
missionService / orchestrator | useMissionsStore (debounced) |
ade.agents.event |
CTO/worker services | CTO tab feed |
ade.lanes.rebaseSuggestions.event / ade.lanes.autoRebase.event / ade.lanes.rebase.event |
rebase services | Lanes + Graph |
ade.project.missing |
projectService | Shell banner |
ade.project.state.event |
projectState | Startup flow |
ade.memory.* events |
memory services | Settings → Memory |
ade.sync.* events |
syncService | Settings → Sync |
Consolidated reads: getFullMissionView returns metadata, run, steps, chat threads, interventions, artifacts, and usage in one IPC — replacing 5+ per-mission-selection calls.
Renderer telemetry events flow back to main: renderer.route_change, renderer.tab_change, renderer.window_error, renderer.unhandled_rejection, renderer.event_loop_stall.
Most services described here live under apps/desktop/src/main/services/<domain>/ in the desktop client's main process. Some are runtime delegations: they front a runtime-owned subsystem (project registry, sync host, agent registry, credential store, multi-project RPC) through a thin local pool plus, where applicable, a legacy in-process fallback. The runtime-side equivalents live under apps/ade-cli/src/services/. Summary:
| Domain | Key files | Role |
|---|---|---|
ai/ |
aiIntegrationService.ts, authDetector.ts, providerConnectionStatus.ts, claudeRuntimeProbe.ts, modelsDevService.ts, compactionEngine.ts, tools/* |
Provider routing, detection, tool definitions, compaction. |
agentTools/ |
agentToolsService.ts |
Agent tool registry metadata surfaced to the renderer. |
appControl/ |
appControlService.ts |
Chrome DevTools Protocol bridge for developer-owned Electron apps. Launches a chat-owned PTY running the user's dev command (or connects to an existing --remote-debugging-port), polls /json for ready CDP targets, attaches a long-lived CdpClient WebSocket, and exposes screenshot / DOM snapshot / hit-test / click / type / scroll / key dispatch / screencast frames. inspectPoint and selectPoint produce AppControlContextItems for the chat composer (DOM packet + screenshot + source-file candidates resolved by findSourceMatches over an indexed tree of project source files). See features/computer-use/app-control.md. |
builtInBrowser/ |
builtInBrowserService.ts |
In-app web browser owned by the main process. Allocates WebContentsView tabs against the shared persist:ade-browser partition (cap 10), positions them over a renderer-supplied bounds rect, drives navigation / tabs / reload / back / forward, attaches the Chrome DevTools Protocol debugger for inspect-mode hit tests, captures screenshots, and emits BuiltInBrowserEventPayloads to subscribers. Consumed by ChatBuiltInBrowserPanel (Work sidebar Browser tab) and by openUrlInAdeBrowser() in the renderer so renderer-side link clicks open inside ADE rather than the system browser. |
automations/ |
automationService.ts, automationPlannerService.ts, automationIngressService.ts, automationSecretService.ts |
Rule lifecycle, NL → rule planner, inbound triggers, per-rule secrets. |
chat/ |
agentChatService.ts, runtimeEvents.ts, buildClaudeV2Message.ts, cursorSdk* (cursorSdkPool.ts, cursorSdkWorker.ts, cursorSdkProtocol.ts, cursorSdkPolicy.ts, cursorSdkSystemPrompt.ts, cursorSdkEventMapper.ts), sessionRecovery.ts |
Agent chat sessions (lane-scoped + mission worker/coordinator). Builds Claude messages, hosts the Cursor SDK in a Node worker pool, formalizes the cross-runtime event vocabulary, recovers sessions on restart, and derives prompt-based lane names for parallel model launches. |
computerUse/ |
computerUseArtifactBrokerService.ts, controlPlane.ts, localComputerUse.ts, agentBrowserArtifactAdapter.ts, syntheticToolResult.ts |
Proof-artifact broker (ingests, owner links, review state, routing), control-plane snapshot helpers, macOS capture capability descriptor, agent-browser payload parser, and the synthetic-tool-result helper used by the Claude compaction path. proofObserver.ts was removed in the rebuild — there is no passive auto-ingest. |
config/ |
projectConfigService.ts, laneOverlayMatcher.ts |
Load/save .ade/ade.yaml + local.yaml; trust enforcement; lane overlays. |
conflicts/ |
conflictService.ts |
Pairwise dry-merge simulation, risk matrix, proposal generation. |
cto/ |
ctoStateService.ts, workerAgentService.ts, workerBudgetService.ts, workerHeartbeatService.ts, linearSyncService.ts, linearIngressService.ts, linearOAuthService.ts, linearRoutingService.ts, linearDispatcherService.ts, linearCloseoutService.ts, flowPolicyService.ts |
CTO identity + core memory; worker agents; Linear sync/ingress/OAuth/routing/dispatcher/closeout. |
devTools/ |
devToolsService.ts |
Probe for git + gh CLI availability. |
diffs/ |
diffService.ts |
Diff computation for file panes. |
feedback/ |
feedbackReporterService.ts |
In-app feedback reporting. Two-stage: prepareDraft generates a structured issue title + labels (AI-assisted when a model is selected, deterministic fallback otherwise) so the user can review before posting; submitPreparedDraft files the GitHub issue. Each submission records generationMode and a generationWarning so the UI can flag deterministic drafts. |
files/ |
fileService.ts, fileWatcherService.ts, fileSearchIndexService.ts |
Workspace file tree, read/write, watch, index. |
git/ |
git.ts, gitOperationsService.ts, gitConflictState.ts |
Low-level git runner, high-level lane-scoped ops, conflict state queries. |
github/ |
githubService.ts |
GitHub REST/GraphQL access; PR CRUD; checks; reviewers. |
history/ |
operationService.ts |
Operation audit records (one row per mutation). |
ios/ |
iosSimulatorService.ts |
macOS-only iOS Simulator backend: tool readiness probes, simctl device + app discovery, build/install/launch with progress events (hardened with simctl bootstatus and simctl install timeouts), screenshot + ADEInspector + accessibility hit-test, IOSurface/Indigo primary streaming and input with idb/simctl/window-capture fallbacks, recovery-only H.264+ffmpeg after idb MJPEG failure, and single-owner chat session locking. The macOS Simulator window placement / capture state probe (getSimulatorWindowState, prepareSimulatorWindowForCapture) lives next to the IPC handlers in ipc/registerIpc.ts because it depends on the active BrowserWindow. See features/ios-simulator/README.md. |
ipc/ |
registerIpc.ts, runtimeBridge.ts, ipcTimeouts.ts |
Single registration point for all IPC handlers. runtimeBridge.ts owns the runtime-facing channels (remote target registry, remote-runtime connect / project list / action dispatch / event stream, local-work checks, LAN discovery) and routes runtime calls through LocalRuntimeConnectionPool or RemoteConnectionPool based on the active window binding. ipcTimeouts.ts carries the shared 30-second handler timeout wrapper. |
jobs/ |
jobEngine.ts |
Event-driven background scheduler for lane refresh + conflict prediction. Coalesced, debounced. |
keybindings/ |
keybindingsService.ts |
User keybindings read/write. |
lanes/ |
laneService.ts, laneEnvironmentService.ts, laneTemplateService.ts, laneProxyService.ts, portAllocationService.ts, autoRebaseService.ts, rebaseSuggestionService.ts, laneLaunchContext.ts, oauthRedirectService.ts, runtimeDiagnosticsService.ts |
Worktree lifecycle, env bootstrap, templates, reverse proxy, port leases, auto-rebase, suggestions, OAuth redirect, diagnostics. |
logging/ |
logger.ts |
File-backed structured logger. |
localRuntime/ |
localRuntimeConnectionPool.ts |
Desktop-side client for the local ade serve daemon. Spawns or attaches to the machine socket, registers local projects with projects.add, dispatches local runtime actions, and installs the background service best-effort in packaged builds. |
macosVm/ |
macosVmService.ts, rfbDirectClient.ts |
Lane-tied macOS VM lifecycle and GUI control. Uses Lume, stores VM records in .ade/cache, stores VNC credentials in .ade/secrets, mounts direct lane roots when safe, otherwise keeps a sanitized rsync mirror, and exposes screenshot/click/type/select through headless VNC or visible-window fallbacks. |
memory/ |
unifiedMemoryService.ts (canonical; listed under memory/memoryService.ts), memoryBriefingService.ts, memoryLifecycleService.ts, batchConsolidationService.ts, embeddingService.ts, embeddingWorkerService.ts, hybridSearchService.ts, episodicSummaryService.ts, knowledgeCaptureService.ts, humanWorkDigestService.ts, proceduralLearningService.ts, compactionFlushPrompt.ts, skillRegistryService.ts, memoryFilesService.ts, memoryRepairService.ts, missionMemoryLifecycleService.ts |
Unified memory subsystem — see §10. |
missions/ |
missionService.ts, missionPreflightService.ts, phaseEngine.ts |
Mission CRUD, preflight validation, phase lifecycle. |
onboarding/ |
onboardingService.ts |
First-run flow, defaults detection, existing lane discovery. |
opencode/ |
openCodeRuntime.ts, openCodeServerManager.ts, openCodeBinaryManager.ts, openCodeInventory.ts, openCodeModelCatalog.ts |
OpenCode server spawn, binary resolution, model discovery. |
orchestrator/ |
See §4.5. | Deterministic mission runtime + intelligent coordinator. |
processes/ |
processService.ts |
Managed-process lifecycle per lane, readiness probes, restart policies. |
projects/ |
adeProjectService.ts, configReloadService.ts, projectService.ts, logIntegrityService.ts, recentProjectSummary.ts, projectBrowserService.ts, projectDetailService.ts |
Project detection + .ade repair/bootstrap, reload on config change, recent-project metadata. projectBrowserService is the in-app directory autocomplete used by the Command Palette project browser (typed-path completion, .git detection, home expansion, system-picker fallback); projectDetailService returns repo metadata (branch, dirty count, ahead/behind, last commit, README excerpt, language mix, lane count, last-opened) for the palette's preview pane. |
prs/ |
prService.ts, prPollingService.ts, prSummaryService.ts, queueLandingService.ts, issueInventoryService.ts, prIssueResolver.ts, prRebaseResolver.ts, integrationPlanning.ts, integrationValidation.ts |
PR CRUD, polling (with per-PR last_polled_at cursor), AI summary cache keyed by (prId, head_sha), stacked-queue landing, issue inventory, AI-assisted resolution, integration planning, and merge-into-existing-lane proposal adoption. |
pty/ |
ptyService.ts |
node-pty spawn, PTY I/O bridging, transcript writing. |
remoteRuntime/ |
remoteTargetRegistry.ts, sshTransport.ts, remoteBootstrap.ts, remoteConnectionPool.ts, runtimeRpcClient.ts |
Saved SSH machines, ssh-agent/key based transport, first-connect runtime upload/version verification, remote project catalog, action dispatch, and reconnect/eviction for remote runtime bindings. |
runtime/ |
tempCleanupService.ts |
Runtime temp cleanup. |
sessions/ |
sessionService.ts, sessionDeltaService.ts |
Terminal session CRUD, post-session delta computation. |
shared/ |
utils.ts, queueRebase.ts, packLegacyUtils.ts, transcriptInsights.ts |
Cross-domain utilities. |
state/ |
kvDb.ts, crsqliteExtension.ts, globalState.ts, projectState.ts, onConflictAudit.ts |
SQLite schema + open, CRR extension loader, global state file, per-project state init. globalState.upsertRecentProject accepts preserveRecentOrder so reactivating an already-known project (by app focus, deep link, etc.) refreshes its lastOpenedAt in place instead of jumping it to the front of the recents list. |
sync/ |
syncService.ts, syncHostService.ts, syncPeerService.ts, syncRemoteCommandService.ts, syncProtocol.ts, deviceRegistryService.ts, syncPairingStore.ts |
Thin delegation to the runtime daemon's sync host plus a legacy in-process fallback. The authoritative sync host now lives in apps/ade-cli/src/services/sync/; the desktop main-process instances default to a non-host viewer role for legacy state. The old in-process host is disabled unless ADE_ENABLE_DESKTOP_SYNC_HOST=1 (diagnostics only). Wire formats — WebSocket envelope, remote command routing, device registry, pairing secrets — are the same across both implementations. |
notifications/ |
apnsService.ts, notificationMapper.ts, notificationEventBus.ts |
APNs HTTP/2 client (ES256 JWT, encrypted .p8), pure domain-event → MappedNotification mapping (13 categories / 4 families), event bus routing to APNs alert pushes + Live Activity update pushes + in-app WS delivery, filtered by per-device NotificationPreferences. |
tests/ |
testService.ts |
Test-suite execution + run history. |
updates/ |
autoUpdateService.ts |
Electron auto-update wrapper around electron-updater. Owns the renderer-visible AutoUpdateSnapshot (`idle |
usage/ |
usageTrackingService.ts, budgetCapService.ts |
Token/cost accounting, budget enforcement. Local cost scans stream bounded recent Claude/Codex JSONL files instead of loading the whole history into memory. |
perf/ |
perfLog.ts, perfIpc.ts, metricsSampler.ts, aggregator.ts |
Opt-in local performance harness. ADE_PERF_RUN_ID opens a JSONL event log, samples Electron process metrics, records IPC durations, accepts renderer perf marks/web-vitals, and aggregates each run into summary.json. |
Startup sequencing: every background service goes through scheduleBackgroundProjectTask() in main.ts, which provides explicit labels, ADE_ENABLE_* env gates, project.startup_task_begin/_done/_enabled/_skipped telemetry, and per-task delays. Integrations stay dormant-until-configured.
Project-init step timing goes through measureProjectInitStep(step, task) — a wrapper that logs project.init_step { projectRoot, step, durationMs } around each hot-path operation (db_open, lane.ensure_primary, ade_rpc.socket_server_start, memory.files.initial_sync, sync.initialize, etc.) so cold-start latency shows up in the logs by phase. The memory-file mirror sync and sync-service initialization are now scheduled through scheduleBackgroundProjectTask rather than awaited inline, gated by ADE_ENABLE_MEMORY_FILE_SYNC and ADE_ENABLE_SYNC_INIT respectively (both default-on).
Shutdown pipeline: main.ts owns a single requestAppShutdown({ reason, exitCode, fastKillFirst?, forceAfterMs? }) path driving a central state machine (shutdownRequested → shutdownPromise → shutdownFinalized). Hooks into before-quit, window close, SIGINT, SIGTERM, process.exit, will-quit, and uncaughtException all funnel through it. runImmediateProcessCleanup() disposes the orchestrator, automations, tests, processes, PTYs, agent chat runtimes, DB flush, and then calls shutdownOpenCodeServers(). A forceAfterMs timer (default 8 s, 5 s for signals/uncaught) hard-exits if cleanup hangs. User-initiated quit (main window close or before-quit) routes through confirmQuitWarning() — a modal dialog that explains that closing will stop OpenCode servers, terminal sessions, and test runs.
On startup the main process also invokes recoverManagedOpenCodeOrphans({ force: true }) (see services/opencode/openCodeServerManager.ts) to reap previous-run OpenCode processes left behind after a crash. Orphan detection matches processes by the managed marker env (ADE_OPENCODE_MANAGED=1) and/or the shared XDG config root, and confirms orphaning either by dead owner PID (ADE_OPENCODE_OWNER_PID) or reparent-to-init. Each acquire of a shared OpenCode server also invokes pruneIdleSharedEntries() which compacts idle entries from older configs (pool_compaction reason).
| Layer | Tech |
|---|---|
| Framework | React 18 |
| Language | TypeScript |
| Router | React Router |
| State | Zustand (global + per-domain) |
| Styling | Tailwind CSS 4 + CSS custom properties |
| Primitives | Radix UI |
| Icons | Lucide React |
| Terminal | xterm.js |
| Editor/Diff | Monaco Editor |
| Graph canvas | React Flow |
| Pane layouts | react-resizable-panels, in-house PaneTilingLayout |
| Virtualization | @tanstack/react-virtual |
Electron renderer runtime does not wrap the app in React.StrictMode. Browser-mock development (outside Electron) still uses Strict Mode. The app uses BrowserRouter on normal http(s) origins and HashRouter inside Electron/file-like contexts; App.tsx also bridges legacy #/route fragments into BrowserRouter paths so old ADE deep links keep working in the browser-hosted dev shell.
apps/desktop/src/renderer/state/appStore.ts (~1,325 lines) — Zustand store holding project, lanes, selected lane, theme, provider mode, keybindings, per-project work-view state. Patterns:
- Narrow selectors on components to minimize re-renders.
refreshLanesaccepts independent lane-status and lane-snapshot flags. Callers can refresh cheap runtime snapshot decorations without recomputing git status, or update git status without rebuilding conflict/rebase/auto-rebase overlays; statusless refreshes preserve the previousLaneStatus/parentStatusin store so the UI does not flicker to unknown git state.- Per-project work-view state keyed by project root (
WorkProjectViewState). Includes the right-edge Work sidebar fieldsworkSidebarOpen,workSidebarTab("git" | "files" | "ios" | "app-control" | "browser"), andworkSidebarWidthPct(clamped 26–55) — persisted alongside the rest of the work-view state underade.workViewState.v1. The sidebar consolidates lane-scoped tools that were previously split across separate floating panes; per-chat iOS / App Control drawers still exist onAgentChatPanebut are suppressed when the chat is mounted as a Work tile so the sidebar owns those surfaces at lane scope. Thebrowsertab is the only sidebar tab that is not lane-scoped — the built-in browser is one shared instance per app. - Store-owned event subscriptions for high-frequency streams (e.g., missions).
projectRevisionis a monotonically incrementing counter bumped insidesetProjectwhenever the active project root actually changes. Long-lived renderer-side caches (most notably the module-level xterm runtime cache inTerminalView.tsx) subscribe to it and tear down any entries whoseprojectRoot/projectRevisionno longer match, so PTYs never bleed between projects. All project-transition paths (refreshProject,openRepo,switchProjectToPath,closeProject) go throughsetProjectto keep the counter honest.
Domain stores co-located with their pages:
useMissionsStore(components/missions/useMissionsStore.ts, ~596 lines) — mission list, selected mission, debounced event handling, store-owned timers.chatDraftStore.ts— draft messages per chat session.
Feature-grouped under apps/desktop/src/renderer/components/:
app/ # shell, App.tsx, TopBar, TabNav, startup, splash
project/ # Play tab, run/test/process controls
lanes/ # list/detail/inspector, stacks, laneDesignTokens.ts
files/ # tree, editor, diffs
terminals/ # TerminalView, WorkViewArea (PaneTilingLayout-backed grid), WorkSidebar, MacosVmPanel, workSessionTiling, LaneCombobox
conflicts/ # risk matrix, simulation, resolution
graph/ # WorkspaceGraphPage (decomposed into nodes/edges/dialogs)
prs/ # PR list/detail, stacked queue, shared/
history/ # operation timeline
automations/ # rule list, pipeline builder
missions/ # MissionsPage + decomposed sub-modules
cto/ # CTO page, identity editor, team panel, pipeline, shared/designTokens.ts
onboarding/ # first-run flows
settings/ # keybindings, agents, data, context, memory, sync
chat/ # AgentChatPane + composer + subpanels
shared/ # MentionInput, shared interactive bits
ui/ # pure presentation primitives
Design tokens have been intentionally trimmed. The CTO design tokens at apps/desktop/src/renderer/components/cto/shared/designTokens.ts are the example style: a small set of Tailwind class constants (cardCls, surfaceCardCls, shellBodyCls, inputCls, labelCls, etc.) and a constrained accent palette (ACCENT.purple/blue/green/pink/amber). Lane design tokens live at lanes/laneDesignTokens.ts and are imported across missions/lanes/PRs/settings.
PaneTilingLayout— recursive pane trees for high-density workspaces, backed by pure ops inpaneTreeOps.ts(reconcilePaneTree,splitPaneAtEdge,swapPanes,detectDropEdge). Trees persist perlayoutIdviawindow.ade.tilingTree; panel sizes persist separately viaDockLayoutStateand are reset whenever the tree mutates.SplitPane/ resizable panels — structured 2/3-pane views.- Work view's grid mode is
PaneTilingLayoutseeded bybuildWorkSessionTilingTree(sessionIds)(inrenderer/components/terminals/workSessionTiling.ts); every session becomes aFloatingPaneleaf withgrid-tilechrome. - The Work surface mounts persistently:
App.tsx'sPersistentWorkSurfacekeepsWorkViewAreain the tree across tab swaps so terminals never tear down on navigation. While inactive the surface is positioned absolute,inert,aria-hidden, opacity-0,pointer-events: none, and pushed behind viaz-index: -1(the olderhiddenattribute was dropping xterm dimensions on reveal). When it goes back to active, it dispatches theWORK_SURFACE_REVEALED_EVENTwindow event on the next animation frame and again 120 ms later so terminal tiles can clear their texture atlas, force-fit, and refocus. - The desktop TopBar project tab strip resolves a per-project favicon via
window.ade.project.resolveIcon(rootPath)and caches the result in a module-localMap. Tabs without an icon (or a missing project root) fall back to theFolderPhosphor glyph; the same component drives the loading-pulse animation when a tab is being switched into or closed. - Layout state persists to SQLite (
layout,tilingTree,graphStatedomains via thekvtable).
Enforced rules (from the stability overhaul):
- All background services go through
scheduleBackgroundProjectTask()— no rawsetTimeoutfor service startup. - New integrations are dormant-until-configured.
- Feature pages stage data: cheapest (list/summary/topology) first, heavy (dashboard/settings/model metadata/overlays) on delay.
- Never mount expensive trees eagerly — settings dialogs, advanced launcher sections unmount when closed.
- Renderer polling is route-scoped; terminal attention only polls on terminal routes; lane panels only poll while live sessions exist. The plain PR list does not fire a GitHub refresh on mount, fetches open external PRs before closed/all history, skips conflict analysis, and defers rebase-needs / auto-rebase polling until the user opens a workflow tab or selects a PR. Workflow PR views batch merge contexts and conflict analysis against metadata-only lane rows instead of running per-PR git/status work. The Lanes page reuses the
LaneSummary.autoRebaseStatussnapshot already in the lane list instead of probing per-lane onLaneGitActionsPanemount; a fallback probe runs only when the snapshot is missing and after a visibility-gated 3.5 s delay. Run'sLaneRuntimeBarkeeps health/process refreshes separate from preview routing / port / OAuth refreshes so process events do not reread routing state. The Work top-bar sync chip refreshes on focus and onsync-statusevents instead of a 5 s interval. The chat composer's Cursor model inventory is fetched lazily —ProviderModelSelectorcallsonOpenon first open of the model catalog, andAgentChatPane.refreshCursorModelInventoryis the only entry point that hitscursorwithactivateRuntime: true. - Shared caches for high-frequency calls (
sessionListCache, GitHub fingerprint-based snapshots). - Memoize expensive renderer computations (
useMemo,React.memo); isolate frequently-refreshing subtrees (e.g., budget footers). Promise.allSettledoverPromise.allfor parallel startup — one failing service must not block others.- Settings sections that surface a snapshot read the cached snapshot on mount (
ade.usage.getSnapshot) instead of forcing a refresh; an explicit Refresh button drives recompute. - Persistence callbacks dedupe against the last-saved value: the workspace-graph view-mode persister tracks the last-loaded preference root and skips the immediate write that the load handler's
setViewModewould otherwise fire.
CLI-launcher and shell-quoting helpers (cliLaunch.ts, shell.ts) live under apps/desktop/src/renderer/ only — the prior apps/desktop/src/shared/ copies were renderer-only in practice and have been removed. The mobile-launcher path (work.startCliSession) was retired with them; iOS launches CLI sessions through host-side actions that don't share renderer modules.
Themes: six shipped themes (e-paper, bloomberg, github, rainbow, sky, pats), persisted in localStorage.ade.theme, applied via data-theme on root. Token-based palettes in apps/desktop/src/renderer/index.css.
renderer/lib/dialogBus.ts— tiny pub/sub that lets the onboarding tour engine (and anyone else) open/close dialogs by a stable id (lanes.create,missions.create, etc.) without prop-drilling. Dialogs subscribe by id; asubscribeAllchannel exists for devtools. Default singleton exportdialogBus.renderer/onboarding/waitForTarget.ts— polls for a DOM target (ref ordata-onboarding-target) with a visibility check so tour steps anchor reliably to async-mounted UI.renderer/onboarding/TourController.ts— imperative driver for the onboarding tour state machine.renderer/onboarding/docsLinks.ts— typed registry of internal/public doc URLs (docs.lanes,docs.missions, …) that tour steps and theHelpMenulink to.renderer/components/onboarding/fx/*— shared motion-FX primitives (ActIntro,AnimatedField,Confetti,GhostCursor,MorphingTree,Spotlight,StaggeredText,TourIllustration) with auseReducedMotionhook. Used by the 13-act first-session tutorial and per-tab tours.
Related UI docs: Terminals UI surfaces, Files and editor, and Onboarding and settings.
| Secret | Location | Protection |
|---|---|---|
| GitHub PAT | .ade/secrets/github/*.bin |
safeStorage.encryptString (OS-backed) |
| API provider keys | .ade/secrets/api-keys.json |
Plaintext 0600 |
| Claude OAuth creds | Claude's own store | Inherited |
| Codex auth tokens | Codex's own store | Inherited |
| macOS Keychain entries | OS Keychain | OS-backed |
| Sync site ID | .ade/secrets/sync-site-id |
Plaintext, never syncs |
| Sync device ID | .ade/secrets/sync-device-id |
Plaintext, never syncs |
| Sync bootstrap token | .ade/secrets/sync-bootstrap-token |
Plaintext, never syncs |
| External-ADE CLI secrets | .ade/local.secret.yaml |
Plaintext, never syncs |
┌──────────────── Main process (trusted) ──────────────┐
│ Full Node access: git, fs, PTY, sqlite, process │
│ ┌────────────────────────────────────────────────┐ │
│ │ Preload bridge (contextBridge) │ │
│ │ window.ade = { /* ~550 typed methods */ } │ │
│ └────────────────────────────────────────────────┘ │
├──────────────── Renderer (untrusted) ────────────────┤
│ React app · no require() · no node · no net │
│ Only path: window.ade.* + CSP │
└──────────────────────────────────────────────────────┘
BrowserWindow hardening:
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: false, // required for preload functionality
preload: "preload.cjs",
}CSP: default-src 'self'; script-src 'self' (no eval, no inline scripts); style-src 'self' 'unsafe-inline' (required for Tailwind); connect-src 'self'; img-src 'self' data:.
Every IPC handler validates its arguments; invalid args return structured errors, never crash. Every handler has a 30s timeout. Every handler emits structured tracing.
- ADE CLI session identity is resolved from env vars and the
initializehandshake. - Role validation: only
cto,orchestrator,agent,external,evaluatoraccepted. - API keys for provider-routed (non-CLI) models are stored via
apiKeyStore.ts.
- Redaction (
shared/utils.tsredactSecrets()) scrubs Bearer tokens, OpenAI/Anthropic API keys (sk-), GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_/github_pat_), Slack tokens (xox*), AWS access keys (AKIA/ASIA), and JSON-embedded sensitive key-value pairs before any log write or AI-context serialization. - Sanitization (
sanitizeStructuredData()) enforces depth limits, redacts sensitive keys, and truncates oversized arrays/strings. - Bounded AI payloads — narrative/proposal/PR description calls use
LaneExportStandardorLaneExportLite+ConflictExportStandard(token-budgeted), not raw pack dumps or transcript slabs. - Path validation (
resolvePathWithinRoot()) resolves symlinks viarealpathSyncbefore containment checks. Applied to lane env init, coordinator tools, process working dirs, sync artifact paths, ADE CLI context file resolution, computer-use artifact ingestion. - Config trust: process/test commands from
ade.yamlrequire SHA-256 hash approval before execution. Commands inlocal.yamlare always trusted. Trust stored inkvwith the config hash as key.
Related trust-boundary docs: Computer-use artifact broker, Computer-use backends, and Configuration schema.
- ADE shells out to the system
gitbinary (not isomorphic-git). Rationale: full feature parity, hook compatibility, native credential handling, performance. - All commands go through
runGit/runGitOrThrowinapps/desktop/src/main/services/git/git.ts(timeout support, structured output parsing). - High-level ops in
gitOperationsService.ts— wrap every mutation inrunLaneOperation(): resolve lane, capture pre-HEAD, record operation, execute, capture post-HEAD, finalize record, fireonHeadChangedif needed.
Each non-primary lane maps to a dedicated worktree:
git worktree add -b ade/<slug>-<uuid8> .ade/worktrees/<slug>-<uuid8> <base_ref>Lane types (per lanes.lane_type):
| Type | Worktree location | Notes |
|---|---|---|
primary |
Project root | The main repo checkout (e.g., main). |
worktree |
.ade/worktrees/<slug>-<uuid8> |
Standard ADE lane. |
attached |
User-specified path | Pre-existing worktree linked to ADE (attached_root_path column). |
Worktree lifecycle: create (60s timeout), archive (DB status only, worktree remains on disk), delete (git worktree remove + optional git branch -D), cascade-delete dependent rows (deltas, sessions, operations, pack index).
- Lanes have
parent_lane_id(self-FK onlanes). Stacks are parent/child chains. - Stack operations: rebase propagation, base-ref resolution (
shared/laneBaseResolution.ts). autoRebaseService.ts+rebaseSuggestionService.ts— automatic rebase proposals when parent moves; user can accept/defer/dismiss.computeLaneStatus()returns{ dirty, ahead, behind }on demand, no caching. Status derivation usesgit status --porcelain=v1andgit rev-list --left-right --count.
- Queue landing (
queueLandingService.ts) — ordered PR landing with rebase propagation. - Conflict prediction —
conflictService.tsusesrunGitMergeTree():git merge-tree --write-tree --messages --merge-base <base> <branchA> <branchB>
- Pairwise dry-merge simulation across all active lanes; output parsed into structured
ConflictOverlapentries. - Triggered on debounced lane/head changes via the job engine; periodic prediction is off by default in dev stability mode.
- Result: risk matrix surfaced on Graph + Conflicts pages, confidence-scored proposals (
high/medium/low) with apply/discard UI.
ensureRelativeRepoPath()rejects empty, null-byte, absolute, and traversal paths.- Force push uses
--force-with-lease, never--force. - Branch-protection support on primary lane.
- Destructive ops (discard, hard reset) require UI confirmation.
Related Git docs: Lanes, Lane runtime isolation, and Pull requests.
Memory is partitioned into three scopes (UnifiedMemoryScope):
| Scope | Visibility | Writer |
|---|---|---|
project |
All runtimes in the project | Agents with active claims + policy grant |
agent |
Runtimes using the same agent identity | Policy-filtered by scope_owner_id = agent.id |
mission |
Runtimes in the current run/mission | Agents in the run, scope_owner_id = run/mission.id |
Legacy aliases user → agent, lane → mission (normalized on read/write). CTO core memory sits outside this model as a per-project always-in-context JSON blob.
Categories: fact, preference, pattern, decision, gotcha, convention, episode, procedure, digest, handoff.
SQLite tables:
unified_memories— primary entries with scope, tier (1/2/3), category, content, importance, confidence, access_score, composite_score, pinned, status (candidate/promoted/archived), dedupe_key, timestamps.unified_memories_fts— FTS4 index kept in sync via triggers. Used for BM25 lexical search.unified_memory_embeddings— vector embeddings per(memory_id, embedding_model). Model:Xenova/all-MiniLM-L6-v2, 384 dim, mean-pooled and normalized.memory_procedure_*— extended metadata for procedure memories (trigger, steps markdown, confidence history, export state).memory_skill_index— file registry for exported/imported skill markdown.knowledge_capture_ledger— dedupe ledger.cto_core_memory_state— per-project CTO blob.
Services under apps/desktop/src/main/services/memory/:
memoryTools.ts— agentmemoryAddtool +resolveAgentMemoryWritePolicy()(pin → tier 1/promoted; strict gate → tier 2/promoted; otherwise candidate at tier 3 with confidence 0.6).episodicSummaryService.ts— buildsEpisodicMemoryafter agent sessions.knowledgeCaptureService.ts— processes interventions/errors/PR feedback into convention/preference/pattern/gotcha.humanWorkDigestService.ts— buildsChangeDigestfrom git commits (no longer writes digest rows to memory; surfaces state instead).memoryFilesService.ts— mirrors promoted project memory to.ade/memory/MEMORY.md+ topic files.proceduralLearningService.ts— identifies repeatable workflows from episode memories.batchConsolidationService.ts— clusters similar memories (Jaccard threshold 0.7), merges via AI, archives originals.compactionFlushPrompt.ts—DEFAULT_FLUSH_PROMPTfed to the Claude SDKPreCompacthook so durable findings are saved before compaction. Claude-only; other providers don't currently expose an equivalent hook.
Quality controls:
- Dedup — Jaccard ≥ 0.85 on normalized content merges into the existing entry; exact
dedupe_keyalways merges. - Write gate —
WriteGateMode(default|strict); strict accepts onlyconvention/pattern/gotcha/decision. - Code-derivable rejection —
rejectCodeDerivableContent()runs heuristic checks:looksLikeRawDiffOrCodeDump,looksLikeRawStackTrace,looksLikeSessionSummary,looksLikeRawGitHistory,looksLikePathDump. - Category allowlist — only the 10 defined categories accepted.
- Memory briefing (
memoryBriefingService.ts) — buildsMemoryBriefingfor mission workers with sectionsl0/l1/l2/mission+ shared facts + tracking arrays. Budgets:lite(3),standard(8),deep(20). Modes:mission_worker,heartbeat,wake_on_demand,prompt_preview. - Direct-source injection — briefing service also injects synthetic memories from
git log,CLAUDE.md/agents.md/AGENTS.md, and.ade/memory/MEMORY.md(all as tier-1 promoted synthetic entries). - Agent tool —
memorySearchsupportslexical(BM25) andhybrid(BM25 + cosine with MMR λ=0.7 for diversity; min 40 vector candidates). - IPC — renderer Settings → Memory panel for list/search/inspect.
- Decay:
nextScore = currentScore * 0.5^(daysSinceAccess / halfLifeDays), halfLife default 30 days. Exempt:preference,convention, pinned. - Sweep (
memoryLifecycleService.ts) — applies decay, demotes below threshold, promotes qualifying candidates, archives above scope limits (project 2000, agent 500, mission 200). Processes in chunks of 250. - Consolidation (
batchConsolidationService.ts) — targets scopes at 80%+ capacity; groups by(scope, scope_owner_id, category); clusters at Jaccard 0.7; merges via AI. - Stale detection — entries not accessed in 24h flagged for demotion.
- Turn-level guard (
universalTools.ts) — forrequiredturns, mutating tools are blocked until the agent callsmemorySearch.
Embedding worker (embeddingWorkerService.ts) processes unembedded entries in batches; states idle → loading → ready (or unavailable). Health polled every 10s in Settings → Memory. Probe cache auto-loads model from local HuggingFace cache when present.
Related memory docs: Memory, Memory storage, and Memory compaction.
ADE does not generate PRD or architecture bootstrap documents. Agent prompts tell models to inspect the repository directly when they need product or architecture context, starting with AGENTS.md, README.md, docs/, package manifests, and relevant source files.
| Call type | Payload |
|---|---|
| Narrative generation | LaneExportStandard (lane, bounded) |
| Conflict proposal | LaneExportLite (lane) + LaneExportLite (peer, optional) + ConflictExportStandard |
| PR description | LaneExportStandard with commit history |
| Mission planning | Memory briefing + mission-scoped data + direct repo inspection guidance |
| Memory briefing (worker turn) | MemoryBriefing (l0/l1/l2/mission sections + shared facts + direct-source injections) |
| Initial context (repo scan) | Targeted file/commit digests |
Proof is intentional. Agents run computer use through whatever tool they already have — Claude's computer_use, Codex shell, a scripted browser, a headless Playwright run, a local screenshot. ADE stays out of that loop. When the agent reaches a checkpoint worth showing, it files an artifact through the broker (directly or via ade proof capture / attach), optionally with a caption. That record is what the drawer UI renders and what reviewers see.
The previous control-plane model — ComputerUsePolicy (off/auto/enabled, allowLocalFallback, retainProof, preferredBackend), per-phase evidenceRequirements, mission-preflight coverage gates, the passive proofObserver that auto-ingested from chat tool_result events, and the Settings > Computer Use panel — was removed. There is one path now: intentional ingest via the broker.
apps/desktop/src/main/services/computerUse/computerUseArtifactBrokerService.ts is the ingest boundary. It accepts ComputerUseArtifactInput[] (path, remote URI, inline text, inline JSON), materializes on-disk sources into the project artifacts dir via secureCopyFromDescriptor (uses O_NOFOLLOW + atomic rename to resist symlink tricks), writes the canonical computer_use_artifacts row, and links to one or more owners (lane, mission, orchestrator_run/_step/_attempt, chat_session, automation_run, github_pr, linear_issue).
Allowed import roots (trust boundary): .ade/artifacts, .ade/tmp, os.tmpdir(), ~/.agent-browser. Other paths are rejected.
Supporting files in the same directory:
controlPlane.ts— buildsComputerUseOwnerSnapshot(recent artifacts + activity) andComputerUseSettingsSnapshot(backend readiness, capabilities) over the broker.localComputerUse.ts— exportsgetLocalProofCaptureCapabilities(), a macOS-only descriptor reporting whetherscreencapture, app launch, and GUI-interaction commands are available.agentBrowserArtifactAdapter.ts— parses agent-browser payloads intoComputerUseArtifactInput[].syntheticToolResult.ts— produces tool-result stubs during Claude compaction so a previously-executed tool response can be re-surfaced without re-running the tool.
Canonical proof kinds: screenshot, video_recording, browser_trace, browser_verification, console_logs.
Canonical tables:
computer_use_artifacts— proof kind, backend name/style, source tool metadata, title/description, URI, storage kind, MIME type, review/workflow state, timestamps.computer_use_artifact_links— cross-domain ownership, so the same artifact can graduate from exploratory chat evidence to a mission artifact to a PR comment without losing provenance.
Channels (under ade.proof.*, renamed from ade.computerUse.*):
ade.proof.listArtifacts,ade.proof.getOwnerSnapshot,ade.proof.routeArtifact,ade.proof.updateArtifactReview,ade.proof.readArtifactPreview, plus aade.proof.eventpush channel.ade proof capture/attach/listin the ADE CLI are the cross-process surface; they call into the broker.
Renderer surfaces:
ChatComputerUsePanel(drawer under the chat composer) andMissionComputerUsePanel/MissionProofPanel(mission detail Proof tab).- Review actions (
accepted/needs_more/dismissed/published) remain as first-class per-artifact actions. - Computer-use readiness moved into
IntegrationsSettingsSection— the standaloneComputerUseSection.tsxis gone.
The sync subsystem is owned by the ADE runtime daemon (apps/ade-cli/src/services/sync/). When a project is opened, its scope creates a sync service inside the runtime; that runtime is the host. The desktop client and iOS client both connect to the same host. Desktop's old in-process host code path is disabled by default and only re-enabled with ADE_ENABLE_DESKTOP_SYNC_HOST=1 for diagnostics.
- Runtime / desktop: native cr-sqlite loadable extension (
.dylib/.dll) loaded viaopenKvDb(...)inkvDb.ts. - iOS: pure-SQL CRR emulation in
apps/ios/ADE/Services/Database.swift—crsql_master,crsql_site_id,crsql_changes, per-table<table>__crsql_clocktables replicated as plain SQLite, with INSERT/UPDATE/DELETE triggers writing Lamport-versioned rows tocrsql_changes. Custom SQLite functions (ade_next_db_version(),ade_local_site_id(),ade_capture_local_changes()) provide trigger context. Changesets are wire-compatible with the runtime's cr-sqlite. - Merge: last-writer-wins per column. Each device has a unique site ID; Lamport timestamps per column.
- Sync API (
AdeDb.sync):getSiteId,getDbVersion,exportChangesSince(version),applyChanges(changes). - Transport: WebSocket on port 8787 (configurable); JSON-framed changesets + zlib compression for large batches; 30s ping/pong. The same envelope channel carries project catalog and project-switch handoff messages before the phone reconnects to a project-specific sync host.
- Host: a runtime daemon on one reachable machine owns live execution side effects (agents, missions, PTYs, processes) for a given project. Stored in the synced
sync_cluster_statesingleton row (brain_device_idis the legacy internal column name; user-facing language is "host"). Transfer requires a clean preflight (no active missions, running turns, live PTYs, running processes). Paused missions, CTO history, and idle chats are durable and survive handoff. - Controllers: other connected devices (phones always; a second desktop optionally). Controllers read synced state and send commands to the host runtime.
- Independent desktops: a second Mac can run its own runtime daemon and work independently through git without joining an ADE sync session. The tracked
.ade/scaffold/config layer makes a clone look like an ADE project immediately.
- App launch reads pairing secret from iOS Keychain.
- Opens WebSocket to host; sends local
db_version; host sends catch-up changesets. hello_okcan include the host's mobile project catalog. The iOS app shows a native project home until an active project is selected, then requests aproject_switch_resultcontaining a project-specific bootstrap token and address candidates.- Bidirectional sync continues; on disconnect, exponential-backoff reconnect with version catch-up.
reconnectIfPossibleis guarded against overlapping runs. - All reads are local and scoped to the active project id — the iOS tab is instant and offline-capable after the selected project's row has hydrated.
- Writes from user actions: write locally, replicate to host. Execution commands (create PR, run command) are routed to the host via the
command/command_ack/command_resultmessage flow. - Sub-protocols: changeset sync, project catalog/switch, file access,
subscribed terminal stream/control, chat stream (live
chat_eventpush from host), command routing, and lane presence announce/release. Command routing includes the Work CLI launcher (work.startCliSession), whose provider command construction is shared with the desktop Work tab throughapps/desktop/src/shared/cliLaunch.ts. - Pairing is a user-set 6-digit PIN stored at
.ade/secrets/sync-pin.jsonon the host. The phone sends the PIN once; the host returns a durable per-device secret. QR payload is v2 (host identity + port + address candidates, no pairing code). - APNs pipeline: iOS registers device tokens (alert + push-to-start + per-activity update) via
SyncService.registerPushToken. The host'snotificationEventBusroutes domain events (chat, PR, CTO, system) toapnsServicefor alert pushes and Live Activity update pushes, filtered by per-deviceNotificationPreferencesstored in the iOS App GroupUserDefaults. - Widgets:
ADEWorkspaceWidget(Home Screen),ADELockScreenWidget,ADEControlWidget(Control Center, iOS 18+) read from a sharedWorkspaceSnapshotin the App Group container.LiveActivityCoordinatormanages the single workspace Live Activity. - Tabs: Lanes, Files, Work, PRs, CTO, Settings.
- LWW per column via Lamport timestamps is the default merge.
ON CONFLICT(...)upserts must target PK only (non-PK UNIQUE does not survive CRR retrofit).- Non-PK merge cases use explicit select-then-update.
- After applying remote changesets that touch
unified_memories, the local FTS index is rebuilt.
.ade/local.secret.yaml(API keys, ADE CLI configs), sync site ID, sync device ID, sync bootstrap token: never sync.- Each device stores its own pairing secret in OS Keychain.
- Linear creds, GitHub tokens, provider API keys stay on the host.
- Commands from non-host devices validated and executed by the host only.
Related sync docs: Sync and multi-device, iOS companion, and Remote commands.
ADE/
├── apps/
│ ├── ade-cli/ # ADE runtime daemon (`ade serve`), `ade` CLI, `ade code` terminal client
│ ├── desktop/ # Electron client (multi-window; local + SSH-bound runtime bindings)
│ ├── ios/ # Native SwiftUI controller (WebSocket to runtime daemon)
│ └── web/ # Marketing + download landing (Vite + React)
├── docs/
│ ├── PRD.md
│ ├── features/
│ ├── perf/
│ ├── plans/
│ └── playbooks/
├── scripts/ # Release, validate, notarize, after-pack (per-platform)
│ # Platform-specific: validate-mac-artifacts.mjs,
│ # validate-win-artifacts.mjs, ade-cli-windows-wrapper.cmd, etc.
├── apps/desktop/vendor/crsqlite/
│ ├── darwin-arm64/
│ └── win32-x64/ # Prebuilt cr-sqlite native binaries per platform
├── .github/workflows/
│ ├── ci.yml
│ ├── prepare-release.yml
│ ├── release.yml
│ └── release-core.yml
├── docs.json # Mintlify public docs config (separate site)
├── package.json # Root test aggregator
└── .ade/ # Self-hosted ADE project state (ignored subset)
Root package.json is a thin aggregator: npm test runs desktop + ade-cli; npm run test:ci runs coverage on desktop + ade-cli.
Per-app scripts:
| App | Key scripts |
|---|---|
apps/desktop |
dev, build (tsup + vite), typecheck, test (vitest), lint (ESLint), dist:mac, dist:mac:universal:signed:zip, notarize:mac:dmg, validate:mac:artifacts, rebuild:native, version:ci, version:release, ade:dev, ade:build, ade:test. |
apps/ade-cli |
dev, build, typecheck, test (typed CLI commands, headless runtime, and Ink Work chat TUI). |
apps/web |
dev, build, preview, typecheck. |
apps/ios |
Xcode project; tests via xcodebuild test / Xcode. |
Stages:
- Install (
installjob) — checkout, setup Node 22, parallelnpm ciacross desktop, ade-cli, and web with a shared cache keyed on those lockfiles. - Parallel checks:
secret-scan— gitleaks on full history.typecheck-desktop—cd apps/desktop && npm run typecheck.typecheck-ade-cli—cd apps/ade-cli && npm run typecheck.typecheck-web—cd apps/web && npm run typecheck.lint-desktop— ESLint onsrc/**/*.{ts,tsx}.test-desktop— 8-way shard matrix:npx vitest run --shard=${{ matrix.shard }}/8across shards 1–8.test-ade-cli— full ade-cli vitest.build— desktop, ade-cli, and web built sequentially after install.validate-docs—node scripts/validate-docs.mjs.
- Gate (
ci-pass) — all required jobs must pass (if: always()with failure/cancelled detection).
Sharding is required because the desktop suite is large enough to be slow in a single process. See memory: always shard test runs.
- Tooling: Vitest with
nodeenvironment,pool: "forks",maxForks: 4, 20s test/hook timeouts. - Config:
apps/desktop/vitest.config.ts(base), plus project-specific configs forunit-main,unit-renderer,unit-sharedwhen needed. - Test locations: colocated with source (
*.test.ts/*.test.tsx) undersrc/**. - Setup:
apps/desktop/src/test/setup.ts(browser/DOM mocks viabrowserMock.ts). - Philosophy (from memory): only tests that carry real value; aggressive removal of brittle UI/render tests; keep mutation + integration coverage solid.
- Smoke tests:
orchestratorSmoke.test.tsfor complex mock orchestration flows;packagedRuntimeSmoke.test.tsfor packaged runtime.
macOS:
npm run dist:mac— notarized .dmg for local distribution.npm run dist:mac:universal:signed— universal x64+arm64 signed builds.npm run dist:mac:universal:signed:zip— zip archive variant.
Windows:
npm run dist:win— x64 installer viaelectron-builder --win --x64, wrapped withvalidate:win:artifacts(preflight) andvalidate:win:release(post-build) checks inapps/desktop/scripts/validate-win-artifacts.mjs.- Windows-only wrappers for the bundled
adeCLI ship inapps/desktop/scripts/:ade-cli-windows-wrapper.cmd(launcher) andade-cli-install-path.cmd(idempotent PATH install helper). The platform-agnostic.shwrapper covers macOS/Linux. - The Windows installer bundles the prebuilt
cr-sqlitenative binary fromapps/desktop/vendor/crsqlite/win32-x64/, a Windows node-pty ConPTY worker, and the@huggingface/transformersONNX Runtime native addon + DirectML DLL used by local-only memory embedding.validate-win-artifacts.mjsasserts each one is unpacked. - GitHub Actions
release-core.ymlbuilds and validates Windows artifacts. The release job picks upWINDOWS_CSC_LINK/WINDOWS_CSC_KEY_PASSWORD(or legacyWIN_CSC_*) from secrets and forwards them as electron-builder'sCSC_LINK/CSC_KEY_PASSWORDto sign the installer andapp.exe; the desktop config sets SHA-256 hashing and the DigiCert RFC3161 timestamp server. When the secrets are absent, the workflow still produces unsigned Windows artifacts. - Ongoing Windows integration lane (rebase with
main, smoke tests, backlog):docs/development/windows-port-lane.md.
Post-packaging hardening (apps/desktop/scripts/):
runtimeBinaryPermissions.cjs— restores exec bits onnode-ptyspawn helpers, Codex vendor binaries, Claude SDK ripgrep helpers; patchesnode-ptyunixTerminal.jsfor ASAR-unpacked paths.after-pack-runtime-fixes.cjs— electron-builder after-pack hook. Covers both platforms: runs the permissions pass on macOS and stages CLI wrappers + runtime shims on Windows.validate-mac-artifacts.mjs/validate-win-artifacts.mjs— per-platform artifact validators; confirm expected binaries and release signing state. Windows signing verification is opt-in with--require-signedorADE_REQUIRE_WIN_SIGNING=1.notarize-mac-dmg.mjs— Apple notarization.
- Internal docs (this directory +
docs/) — for engineers and agents. Not published. - Public docs site — Mintlify, configured in
docs.jsonat repo root. Content lives alongside the repo (introduction.mdx,quickstart.mdx,welcome.mdx,key-concepts.mdx, plus subdirsgetting-started/,guides/,lanes/,chat/,missions/,cto/,pull-requests/,configuration/,tools/,computer-use/,automations/,ai-tools/). Thememaple, brand primary#7C3AED. - Doc validation:
scripts/validate-docs.mjsruns in CI to catch broken links / structure drift.
- Main-process logger —
apps/desktop/src/main/services/logging/logger.ts(createFileLogger). Writes structured JSONL to~/.ade/logs/<project>/ade-main.log. Categories:ipc.*,project.startup_task_*,renderer.*, per-service telemetry. - Redaction — all log writes pass through
redactSecrets()/sanitizeStructuredData(). - Retention — local, indefinite until user clears.
- IPC tracing — every handler emits
ipc.invoke.begin/ipc.invoke.done/ipc.invoke.failedwith call ID, channel, window ID, duration, summarized args. Mandatory for new handlers. - Renderer lifecycle —
renderer.route_change,renderer.tab_change,renderer.window_error,renderer.unhandled_rejection,renderer.event_loop_stall. Mandatory for new surfaces that introduce novel lifecycle transitions. - Startup tasks —
project.startup_task_enabled,project.startup_task_skipped,project.startup_task_begin,project.startup_task_donewith durations. - Usage tracking —
usageTrackingService.ts+budgetCapService.tsaccount for tokens and cost per provider/model/call-type; surfaced in Missions UI and the top-bar Usage popup (HeaderUsageControl→UsageQuotaPanel+ collapsibleBudgetCapEditor). - Local perf runs —
scripts/perf-launch.mjs/scripts/run-perf-scenario.mjslaunch ADE with a run id, feed renderer scenarios, and collect JSONL events plussummary.jsonunder~/.ade/perf-runs/<runId>/. This is local-only diagnostics, not external telemetry. - No external telemetry — ADE does not ship analytics to any cloud service. All telemetry is local.
- Every cleanup step is
try/catchisolated — one failing service must not block shutdown. - IPC handlers return structured errors, never crash the renderer.
- Mission and CTO UI components use try/catch around async loads with
isLoading/errorstate and retry actions. - Graceful degradation: when no provider is configured, AI surfaces show explanatory disabled state rather than spinning.
- Explicit fallbacks: Linear sync skips when no credentials/workflows; Linear ingress stays dormant without config; trivial session summaries skip AI entirely.
- Dev tools probe —
devToolsService.tschecks forgitandghCLI availability at startup, surfacing warnings in UI. - Port allocation —
portAllocationService.tsmanages per-lane port leases with orphan recovery. - Runtime diagnostics —
runtimeDiagnosticsService.tssurfaces lane launch context and runtime state. - Embedding health — polled at 10s intervals in Settings → Memory (raised from 1.5s to reduce renderer churn).
- Sync telemetry —
sync_cluster_state+ device registry surfaced in Settings → Sync. - Operation timeline —
operationService.ts+ History page provide full audit trail for debugging and undo. - Shutdown sequence:
- Stop head watcher + background timers.
- Dispose pollers and ingress services.
- Stop file watchers, tests, managed processes.
- Dispose PTYs and agent chat sessions.
- Dispose sync service (stop host, disconnect peer).
- Flush SQLite before service disposal begins (durable writes first).
- Per-service
try/catch-isolated dispose. - Final SQLite flush + close.
- Product spec · PRD.md
- Runtime and remote bindings · Remote runtime
- Terminal client · ADE Code
- Project dashboard / Run tab · Project Home
- Lanes and Git isolation · Lanes
- Agent chat · Chat
- Missions and orchestration · Missions
- Pull requests and queues · Pull Requests
- Multi-device sync and iOS · Sync and Multi-device
- Terminal sessions and Work · Terminals and Sessions
- Computer-use proof · Computer Use
- Memory · Memory
- Settings and onboarding · Onboarding and Settings
- Feature index · features/