ADE does not ship a standalone "agents hub" page; instead, agent behavior is delivered through three runtime surfaces: the CTO (persistent identity inside the CTO tab), worker agents (named employees with their own identity and adapters), and regular chat sessions (ephemeral agents bound to a lane). This feature folder documents the agent identity model, persona system, and the tool registry / ADE CLI integration that all three share.
| Path | Role |
|---|---|
apps/desktop/src/main/services/cto/ctoStateService.ts |
CTO identity, core memory, session logs, subordinate activity, immutable doctrine, personality overlays. The CTO's everything. |
apps/desktop/src/main/services/cto/workerAgentService.ts |
Worker identity and core memory CRUD; validates adapterType against the three-entry allowlist (claude-local, codex-local, process). Persists agent_identities rows and .ade/agents/<slug>/ files. |
apps/desktop/src/main/services/cto/workerHeartbeatService.ts |
Heartbeat scheduling for workers (wake-on-demand + periodic intervals). |
apps/desktop/src/main/services/cto/workerBudgetService.ts |
Monthly budget tracking (budgetMonthlyCents, spentMonthlyCents). |
apps/desktop/src/main/services/cto/flowPolicyService.ts |
Worker flow policies (guardrails, approval requirements). |
apps/desktop/src/main/services/cto/workerAdapterRuntimeService.ts |
Adapter lifecycle for the three supported worker adapter types. |
apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts |
CTO-only tools (spawnChat, mission control, worker management, Linear dispatch). |
apps/desktop/src/main/services/agentTools/agentToolsService.ts |
Detects external CLI tools (Claude Code, Codex, Cursor, Aider, Continue) on PATH. |
apps/ade-cli/src/cli.ts |
Agent-focused ade command surface and text/JSON output formatters. Includes the ade ios-sim (alias ade ios, ade simulator) family — see iOS Simulator feature, the ade --socket app-control ... driver for live Electron apps, and the ade --socket browser ... driver for the in-app browser (browser panel, browser open <url> [--no-panel], browser new-tab --background, browser switch, browser close, plus selection / inspect commands). ade chat create --provider codex --model <id> --fast opts a new Codex session into the fast service tier; ade shell start --lane <id> --chat-session <chatId> (or ADE_CHAT_SESSION_ID from the env) attaches a tracked shell to an existing chat so ade --socket terminal read --chat-session "$ADE_CHAT_SESSION_ID" --text resolves to it. |
apps/ade-cli/src/adeRpcServer.ts |
Private ADE action RPC: registers actions, handles JSON-RPC, applies session-identity-based filtering, and builds lane-scoped ADE guidance / ADE_AGENT_SKILLS_DIRS for worker CLI launches. |
apps/desktop/src/main/services/cli/adeCliService.ts |
Desktop-side install / status / uninstall surface for the ade launcher. Owns the install-target path resolution and the optional shell-rc PATH append. |
apps/desktop/src/shared/adeCliGuidance.ts |
Canonical agent-prompt guidance builder for finding and using ade, reading bundled ADE skills on demand, using socket-backed live surfaces, registering proof, and cleaning up started processes. |
apps/desktop/src/shared/agentSkillRoots.ts |
Resolves and formats the ADE Agent Skills roots injected into prompts and CLI environments. Active lane worktrees are preferred before inherited app / packaged roots so launched agents see lane-local bundled skills first. |
apps/desktop/src/shared/ctoPersonalityPresets.ts |
CTO personality overlays (strategic, professional, hands_on, casual, minimal, custom). |
apps/desktop/src/shared/types/agents.ts |
AgentIdentity, AgentCoreMemory, AgentRole, AdapterType, adapter configs. |
apps/desktop/src/shared/types/cto.ts |
CtoIdentity, CtoCoreMemory, CtoCapabilityMode, CtoPersonalityPreset. |
One persistent identity per project. Carries a structured CtoIdentity
document (name, persona, personality preset, communication style,
constraints, model preferences, memory policy) and a small structured
CtoCoreMemory document (project summary, critical conventions, user
preferences, active focus, notes).
See identity-and-personas for the full
identity flow; see tool-registration for the
tool palette (universal + workflow + CTO operator + Linear, plus
memoryUpdateCore).
Zero-or-more named agent identities per project, stored as
agent_identities rows plus .ade/agents/<slug>/* files. Each worker
has its own role (engineer, qa, designer, devops, researcher,
general), adapter type, runtime policy, heartbeat schedule, budget,
and core memory.
Workers are activated either on-demand (chat session) or on a scheduled heartbeat; when active they receive a memory briefing assembled from project, agent, and mission memories.
Ephemeral sessions bound to a lane. No identity document, no persistent core memory -- the session state lives in the chat transcript and resumes across restarts but has no long-term persona. The CTO and workers are just identity sessions layered on top of the same chat runtime.
When a chat session targets a provider whose CLI (Claude, Codex,
Cursor, Droid) is missing or not authenticated on the active runtime,
the chat surfaces an inline AgentCliAuthCard
(apps/desktop/src/renderer/components/chat/AgentCliAuthCard.tsx).
The card carries an AgentCliAuthCardInfo payload built by the chat
service via classifyAgentCliError from
apps/ade-cli/src/services/agentRegistry.ts — the same registry the
runtime uses to recognise "binary not on PATH" vs "needs login"
patterns from a CLI's stderr.
The card renders two action rows: an Install row when the CLI is missing, and an Authenticate row in either case. Each row has:
- A copy-to-clipboard chip for the canonical install / auth command
(e.g.
claude /login,codex login,cursor-agent login). - A Run button that opens a tracked PTY in the active lane
(
window.ade.pty.create) withstartupCommandset to that command andtracked: trueso the new terminal lands in the chat's terminal drawer.
The crucial property is that the install/login command runs in the
active runtime — the runtime the chat session is bound to. A
desktop window bound to a remote ade serve daemon launches the
install/auth in a PTY on that remote machine, not locally. So a user
pairing with a remote runtime sets up claude / codex / cursor on
the remote host without leaving the chat or SSHing in.
The card also shows the runtime name (when known) in its body copy
("Authenticate the CLI on darwin-mini, then retry the chat.") so the
operator can see which machine the install will land on before
clicking Run.
type CtoIdentity = {
name: string;
version: number;
persona: string; // freeform persona text
personality?: CtoPersonalityPreset; // strategic | professional | hands_on | casual | minimal | custom
customPersonality?: string; // used when personality = "custom"
communicationStyle?: {
verbosity: "concise" | "detailed" | "adaptive";
proactivity: "reactive" | "balanced" | "proactive";
escalationThreshold: "low" | "medium" | "high";
};
constraints?: string[];
systemPromptExtension?: string;
onboardingState?: CtoOnboardingState;
modelPreferences: {
provider: string;
model: string;
modelId?: ModelId;
reasoningEffort?: string | null;
};
memoryPolicy: {
autoCompact: boolean;
compactionThreshold: number;
preCompactionFlush: boolean;
temporalDecayHalfLifeDays: number;
};
updatedAt: string;
};And the separate core memory document:
type CtoCoreMemory = {
version: number;
updatedAt: string;
projectSummary: string;
criticalConventions: string[];
userPreferences: string[];
activeFocus: string[];
notes: string[];
};Persisted in two places:
- SQLite:
cto_identity_state/cto_core_memory_statetables (one row per project; versioned, JSON payload). - Filesystem:
.ade/cto/identity.json/.ade/cto/core-memory.json(written atomically viawriteTextAtomic).
Reconciliation on startup prefers whichever has the higher version;
version-based reconciliation handles the case where the JSON file is
edited externally.
type AgentIdentity = {
id: string;
name: string;
slug: string;
role: "cto" | "engineer" | "qa" | "designer" | "devops" | "researcher" | "general";
title?: string;
reportsTo: string | null;
capabilities: string[];
status: "idle" | "active" | "paused" | "running";
adapterType: "claude-local" | "codex-local" | "process";
adapterConfig: AgentAdapterConfig; // adapter-specific
runtimeConfig: {
heartbeat?: HeartbeatPolicy;
maxConcurrentRuns?: number;
};
linearIdentity?: AgentLinearIdentity; // optional Linear user mapping
personality?: string;
communicationStyle?: string;
constraints?: string[];
systemPromptExtension?: string;
budgetMonthlyCents: number;
spentMonthlyCents: number;
lastHeartbeatAt?: string;
createdAt: string;
updatedAt: string;
deletedAt?: string | null;
};
type AgentCoreMemory = {
version: number;
updatedAt: string;
projectSummary: string;
criticalConventions: string[];
userPreferences: string[];
activeFocus: string[];
notes: string[]; // same shape as CtoCoreMemory
};Persisted at:
- SQLite:
agent_identitiestable. - Filesystem:
.ade/agents/<slug>/identity.json,.ade/agents/<slug>/core-memory.json,.ade/agents/<slug>/daily/<YYYY-MM-DD>.md(daily logs),.ade/agents/<slug>/MEMORY.md(long-term brief, auto-generated).
Workers dispatch through one of three adapter types — there are no
others, and apps/desktop/src/shared/types/agents.ts types
AdapterType as exactly "claude-local" | "codex-local" | "process":
| Adapter | Config | Purpose |
|---|---|---|
claude-local |
ClaudeLocalAdapterConfig (model, cwd, cliArgs, instructions, timeout) |
Spawns claude CLI locally. |
codex-local |
CodexLocalAdapterConfig (model, cwd, cliArgs, reasoningEffort, timeout) |
Spawns codex CLI locally. |
process |
ProcessAdapterConfig (command, args, cwd, env, timeout, shell) |
Generic managed subprocess; the catch-all for wrapping anything that isn't claude / codex. |
The worker service forwards the correct adapter config to the orchestrator when the worker is activated.
CtoCapabilityMode (also applies to workers via capabilityMode on
session logs):
full_tooling-- the session connects to the ADE CLI over the ADE CLI/action bridge and has full action access.fallback-- the ADE CLI/action bridge is unavailable; the session gets only its adapter's built-in tool set.
capabilityMode is persisted per session log so history shows which
mode the agent actually ran in.
Each agent type gets a distinct tool palette. The full breakdown is in tool-registration; summary:
| Tier | CTO | Worker | Regular chat | Coordinator |
|---|---|---|---|---|
| Universal (read, write, bash, memory, web, todo) | yes | yes | yes | yes |
| Workflow (createLane, createPR, captureScreenshot, reportCompletion, PR issue resolution) | yes | no | yes | no |
| CTO operator (spawnChat, missions, worker management, Linear) | yes | no | no | no |
| Coordinator (spawn_worker, skip_step, complete_mission, etc.) | no | no | no | yes |
| Linear tools | yes (when connected) | no | no | no |
memoryUpdateCore |
yes | yes | no | no |
Standalone chat sessions (sessions without mission/run/step/attempt
context) additionally get spawn_agent and all coordinator tools
hidden from both tool listing and tool execution, enforced at the ADE CLI
server boundary. See tool-registration.
All three surfaces use the same system-prompt builder
(buildCodingAgentSystemPrompt in
apps/desktop/src/main/services/ai/tools/systemPrompt.ts) with
different inputs:
- CTO:
systemPrompt.tsoutput is prefixed by theIMMUTABLE_CTO_DOCTRINE,CTO_MEMORY_OPERATING_MODEL, andCTO_ENVIRONMENT_KNOWLEDGEblocks fromctoStateService.ts, plus the active personality overlay and user-defined persona /systemPromptExtension. - Worker: similar structure, but with
AgentCoreMemoryreconstructed into the context and the worker's persona + constraints + system prompt extension. - Regular chat: no identity prefix; just lane context, memory tools guidance, workflow tools guidance, and permission-mode framing.
The prompt branches on which tool names are present (see Chat Tool System) so renaming a tool silently changes the prompt.
workerHeartbeatService.ts schedules worker activations:
HeartbeatPolicy.enabledgates scheduling entirely.intervalSeccontrols periodic wake-up.wakeOnDemandallows the CTO or Linear dispatcher to activate the worker outside the schedule.activeHoursrestricts heartbeats to a time window (with timezone).
When a worker activates, the runtime:
- Assembles a memory briefing (
memoryBriefingService.ts). - Resolves the adapter config.
- Dispatches through the orchestrator or spawns a chat session (via
the CTO's
spawnChattool) depending on the activation kind. - Records a
worker_agentsruntime row and anAgentSessionLogEntry.
Both CTO and workers maintain append-only session logs:
- CTO:
cto_session_logstable. - Workers:
worker_agent_runs(runtime rows) and session log entries persisted under.ade/agents/<slug>/.
Each entry carries: session id, summary, started/ended timestamps,
provider, model id, capability mode, and a previous-hash pointer
(prevHash) so integrity of the log can be verified. The log integrity
service (projects/logIntegrityService.ts) computes and verifies these
hashes.
CTO sessions include a CtoSubordinateActivityEntry feed surfaced in
the CTO surface. Entries record chat turns and worker runs with:
agentId,agentNameactivityType:chat_turnorworker_runsummary,sessionId,taskKey,issueKey
This feed is the CTO's awareness of what workers have been doing; the
CTO can proactively check it at the start of each session via the
Daily Context protocol.
The CTO writes append-only daily logs at
.ade/cto/daily/<YYYY-MM-DD>.md. Workers have their own at
.ade/agents/<slug>/daily/<YYYY-MM-DD>.md. Daily logs are included in
the continuity context for the current day, providing within-day
session-to-session continuity without the full history weight.
Defined in apps/desktop/src/shared/ipc.ts (search ade.cto.* and
ade.workers.*). Handlers in registerIpc.ts.
Representative channels:
| Channel | Purpose |
|---|---|
ade.cto.getState |
Fetch CtoSnapshot (identity + core memory + recent sessions + subordinate activity). |
ade.cto.updateIdentity |
Patch identity fields. |
ade.cto.updateCoreMemory |
Patch core memory fields. |
ade.cto.ensureSession |
Create or fetch the CTO's persistent chat session. |
ade.cto.appendSessionLog |
Append to the session log (internal; called by agentChatService on completion). |
ade.cto.getSystemPromptPreview |
Generate a preview of the CTO's system prompt for the UI. |
ade.workers.list |
List worker identities. |
ade.workers.upsert |
Create or update a worker. |
ade.workers.remove |
Soft-delete a worker. |
ade.workers.getCoreMemory |
Fetch a worker's core memory. |
ade.workers.updateCoreMemory |
Patch a worker's core memory. |
ade.workers.triggerWakeup |
Force a worker heartbeat. |
ade.workers.heartbeatStatus |
Current heartbeat schedule + last fire. |
ade.workers.getBudget |
Monthly budget + spend. |
- Core memory version reconciliation. On startup,
reconcileCoreMemoryOnStartup()compares the SQLiteversionto the filesystemversionand picks the newer. Concurrent edits (user editing the JSON file while ADE is running) can create version inversions; the reconciler writes the loser back to sync but loses the other side's changes. Always edit through the UI or theupdateCoreMemoryIPC when possible. - Post-compaction identity re-injection. CTO and worker identity
sessions re-inject
buildReconstructionContext()after chat context compaction; missing this path loses the persona mid-session. The wiring lives inagentChatService.tsand relies on an explicitrefreshReconstructionContext()call from the compaction handler. - Subordinate activity ordering.
appendCtoSubordinateActivityprepends to the feed and caps at N entries. Rapid concurrent writes can race; writes go throughctoStateServicewhich serialises via the db layer. - Personality preset lookup.
getCtoPersonalityPreset()falls back to the first preset (strategic) on unknown input. Removing or renaming a preset id would silently remap existing CTO identities to the default. Keep preset ids stable. - Worker slug uniqueness.
slugify(input)can collide; the service guarantees uniqueness by appending-2,-3, etc. when the slug already exists. Renaming a worker does not move its filesystem directory. - Adapter config secret policy.
assertEnvRefSecretPolicyrejects raw secrets embedded in adapter configs, forcing${env:VAR_NAME}references. Bypassing this check (e.g., via direct SQL writes) allows secrets to leak into transcripts. - Daily log integrity hashes. Each session log entry carries
prevHashlinking back to the previous entry. Manually deleting a row from the table breaks the chain;logIntegrityServicewill detect the break on next verification but won't rebuild the chain. - Adapter type ↔ capability mode.
claude-localandcodex-localcan run infull_toolingmode only when the ADE CLI socket server is running; if the socket fails, workers silently fall back tofallbackmode. Surface this via capability mode in the session log entry. - Standalone-chat tool filtering at ADE CLI boundary. The filter is
applied in
apps/ade-cli/src/adeRpcServer.tsbased on theinitializepayload's identity. A client that omits identity falls back toexternalrole with minimal tool access; a client that forges a mission id could theoretically get elevated access, so the socket is local-only and the proxy cannot be externally reached.
- Identity and Personas -- how CTO and worker identity is stored, reconciled, and injected into sessions, including the personality preset system and immutable doctrine.
- Tool Registration -- ADE CLI integration, bundled proxy, tool registry, role-based filtering, capability mode fallback.
- Chat README -- session lifecycle, identity session filtering.
- Chat Agent Routing -- provider and model selection for agents.
- Memory README -- memory briefing, core memory, and scope rules.
- Chat Tool System -- details of universal, workflow, and coordinator tiers.