Architecture
EstaCoda is a TypeScript agent runtime for Node.js. It executes provider-backed sessions through a CLI and multiple remote channels. This page explains how the system is structured, where state lives, and which surfaces are stable, experimental, or unsupported in v0.1.0.
The goal of this page is practical orientation: what owns what, where to inspect it, and which runtime paths are part of the current baseline.
What EstaCoda is
At its core, EstaCoda is a turn-based agent loop that:
- Receives input from a surface such as CLI, Telegram, Discord, Email, or WhatsApp
- Assembles a layered prompt from session history, memory, skills, project context, and live input
- Sends the prompt to a configured provider route
- Executes tool calls under the active security policy
- Returns output to the active surface
- Persists session state, memory, approvals, artifacts, and trajectory data where applicable
The loop is explicit construction, explicit delegation, and explicit persistence. Runtime behavior should be inspectable from code and local state.
Entrypoints and surfaces
| Entrypoint | Role | Maturity |
|---|---|---|
src/index.ts | Boot flow, command dispatch, session restoration | live-proven |
src/cli/cli.ts | CLI command parsing and dispatch | live-proven |
src/cli/session-loop.ts | Interactive terminal session | live-proven |
src/channels/gateway-runner.ts | Gateway diagnostics helpers | smoke-tested |
src/channels/telegram-adapter.ts | Telegram adapter | live-proven |
src/channels/discord-adapter.ts | Discord adapter | present-not-live-proven |
src/channels/email-adapter.ts | Email adapter | present-not-live-proven |
src/channels/whatsapp-adapter.ts | WhatsApp adapter | operational-with-external-risk |
CLI and Telegram are the strongest live-proven surfaces for v0.1.0. Discord and Email adapter code exists and is test-backed, but live production validation is deployment-specific. WhatsApp is setup-backed and operational through an isolated bridge, but it remains gated because Baileys is an unofficial API.
Profile and state boundaries
EstaCoda organizes operator-owned configuration around profiles. A profile is the active bundle of provider routes, credentials, memory files, installed skills, channel state, logs, and runtime caches for one operator context.
What lives under a profile
A typical profile directory contains:
~/.estacoda/profiles/<id>/
| Path | Use |
|---|---|
config.json | Provider routes, channel config, security mode, and feature settings |
.env | Profile-local secret values such as API keys and bot tokens. Not shown in setup review output. |
auth.json | OAuth token records, such as Codex OAuth |
USER.md | User preferences and stable user guidance |
SOUL.md | Protected identity and safety guidance |
MEMORY.md | Durable working memory and promoted facts |
promotions.json | Memory promotion metadata |
skills/ | Profile-installed skills |
cron/ | Profile-local scheduled jobs |
gateway/ | Channel session state, gateway handoff state, and adapter state |
logs/ | Runtime logs |
channel-media/ | Downloaded channel media |
audio-cache/ | Audio artifacts and voice cache |
image-cache/ | Image generation and image input cache |
temp/ | Profile-local temporary files |
Not every file or directory exists before the related capability runs.
What is global
| Path | Use |
|---|---|
~/.estacoda/active-profile.json | Currently selected profile |
~/.estacoda/sessions.sqlite | Session database, with rows scoped by profile_id |
~/.estacoda/memory/shared/ | Shared memory across profiles |
~/.estacoda/trust.json | Workspace trust grants keyed by directory |
~/.estacoda/workspace-approvals.json | Persistent workspace approval grants keyed by directory and action |
~/.estacoda/packs/ | Installed pack state |
~/.estacoda/bin/ | Managed helper binaries |
What is workspace-local
| Path | Use |
|---|---|
AGENTS.md | Project context and coding instructions |
Trust is global state with directory-scoped entries. It is not stored inside the workspace. A profile selects configuration, credentials, memory, and channel identity. Workspace trust gates local tool behavior for a directory. They do not override each other.
Runtime composition root
createRuntime() in src/runtime/create-runtime.ts is the composition root. It explicitly creates stores, providers, tools, skills, prompt dependencies, runtime components, and the agent loop. There is no dependency injection container. Construction is visible in code and happens in a fixed order.
Construction order:
- Global and profile state paths
- State stores: memory, sessions, artifacts, cron, workflow, and gateway state
- Provider registry, provider executor, and auxiliary route resolver
- Tool registry and built-in tool providers
- Skill registries for official, profile, and external skills
- Prompt dependencies: prompt cache, project context, context expansion, and memory context
- Runtime execution components such as
ProviderTurnLoop,ToolPlanRunner,SkillPlaybookRunner, and native tool execution AgentLoopBuilder, which assembles session-scoped loop dependenciesDefaultChildAgentLoopFactory, which builds child loops for delegated subagent workAgentLoop, which coordinates turn boundaries, runtime events, continuation, cancellation, and persistence
Key composition rules:
- Official skills load first. Profile-installed and configured external skills are layered after them.
- Visible skill catalog is filtered per session using runtime conditions.
- Tools are registered in phases so session-bound tools receive the correct runtime context.
- Auxiliary model routes use the same provider registry and executor path as primary model routes.
- Configured MCP servers are loaded during runtime creation and stopped during runtime disposal.
- Child agent loops are built through the child loop factory, not by bypassing the runtime composition path.
Core subsystems
Provider layer
The provider layer has three parts: model catalog, provider registry, and execution.
| Part | Role |
|---|---|
| Model catalog | Resolves known model profiles, capabilities, and fallback metadata |
ProviderRegistry | Registers provider adapters and exposes route lookup |
ProviderExecutor | Executes provider requests, collects streaming tokens, assembles tool calls, and handles fallback routes |
EstaCoda routes provider calls through configured provider adapters, including OpenAI-compatible chat completions and OpenAI Responses where configured. Provider selection depends on configured provider IDs, route preferences, model capability, and credential readiness.
Auxiliary routes resolve through the same infrastructure as primary model routes. Supported route names include vision, compression, assessor, profile_context, web_extract, session_search, skills_library, mcp, memory_flush, and delegation. These are preference routes, not separate runtimes.
See Provider Reference for provider maturity labels.
Tool layer
ToolRegistry registers built-in tools at load time and MCP-discovered tools during runtime creation. ToolExecutor runs concrete tool actions under the active security policy. ToolPlanRunner converts provider tool calls into executable plans, manages safe-tool concurrency, and enforces failure caps.
Tool risk classes drive gating:
| Risk class | Meaning |
|---|---|
safe | Low-risk read or local computation |
caution | Needs care or may expose local state |
external-side-effect | Can affect external systems |
irreversible | Can make hard-to-reverse changes |
Skill layer
Skills load from three sources:
| Source | Location | Mutability |
|---|---|---|
official | Bundled in repo | Read-only |
profile | ~/.estacoda/profiles/<id>/skills/ | Mutable |
external | Configured external roots | Read-only |
Visibility is session-stable and refreshed on /reset or a new session. Skill execution is provider-backed by default, with deterministic fallback behavior for no-provider sessions.
Memory layer
MemoryStore manages bounded memory files. LocalMemoryProvider reads and writes profile-local memory files. MemoryRecallOrchestrator prepares per-turn memory context for prompt assembly.
Recall, external recall, and compression summaries are reference-only context. They are not trusted as authoritative instructions.
Channel layer
ChannelGateway is the generic adapter bridge. It handles auth, allowlists, session mapping, progress delivery, approval prompts, and command routing. Adapters do not mutate approval state or runtime cache directly.
DeliveryRouter is the normalized delivery path for channels. It handles multi-target delivery, text truncation, error persistence, and artifact routing.
Security layer
EstaCoda uses a capability-first security boundary. Approval modes include strict, adaptive, and open. adaptive is the default. /yolo toggles open mode for the session, but it cannot bypass the hardline floor.
Persistent approvals match on normalized targetKey. Display summaries are not the approval boundary.
The hardline floor covers high-risk actions such as broad recursive deletes, destructive disk operations, shutdown or reboot commands, fork-bomb or kill-all patterns, secret reads, pipe-to-interpreter installs, and git force-pushes.
Setup and onboarding
The setup subsystem owns reviewed first-run setup, repair flows, optional capability setup, config editing, and verification.
| Area | Code |
|---|---|
| Onboarding Wizard | src/setup/onboarding-wizard/ |
| Setup Editor | src/setup/config-editor/ |
| Review and apply | src/setup/review/ |
| Verification | src/setup/verification.ts |
| Optional capabilities | src/setup/optional-capability-flow.ts |
| WhatsApp setup | src/setup/whatsapp-setup-flow.ts |
Setup writes profile-local configuration and secrets. It shows a review before applying changes, and raw secrets are not displayed in review output.
Delegation and subagents
Delegation lets a parent session spawn bounded child agent sessions. Child work is constrained by toolset policy, model route policy, timeout and budget settings, and file-state checks.
| Component | Role |
|---|---|
DelegationManager | Starts and tracks delegated child work |
SubagentRegistry | Tracks active subagents |
ChildRunner and BatchRunner | Execute one or more child sessions |
ProgressRelay | Relays safe child progress to the parent |
file-state-guard and file-state-tracker | Warn when parent file reads may be stale after child writes |
toolset-security | Enforces child tool boundaries |
Child sessions use DefaultChildAgentLoopFactory so delegated work goes through the same runtime construction path as normal sessions.
Workflow engine
The workflow subsystem runs durable multi-step work. It is separate from a single provider turn and can persist progress through SQLite-backed workflow state.
| Component | Role |
|---|---|
workflow-engine.ts | Executes workflow plans |
sqlite-workflow-store.ts | Persists workflow state |
workflow-command-dispatcher.ts | Handles workflow commands |
workflow-lock-service.ts | Coordinates workflow locks |
workflow-restart-recovery.ts | Recovers restartable workflow state |
skill-playbook-to-workflow-plan.ts | Converts skill playbooks into workflow plans |
Workflows are implemented as a durable orchestration surface. They are not the default path for normal chat turns.
Packs and distribution
Packs are installable bundles. The pack subsystem validates pack metadata, permissions, risk, and installation behavior before a pack is accepted.
| Component | Role |
|---|---|
pack-installer.ts | Installs packs |
pack-registry.ts | Tracks installed packs |
pack-validator.ts | Validates pack shape |
pack-permission-validator.ts | Checks declared permissions |
pack-risk-classifier.ts | Classifies pack risk |
pack-force-audit-log.ts | Records forced installs |
Packs are reviewable runtime extensions. They do not bypass normal permission and trust boundaries.
Lifecycle management
Lifecycle code handles installation state, updates, uninstall, version resolution, and state preservation.
| Component | Role |
|---|---|
install-method.ts | Detects how EstaCoda was installed |
update-engine.ts | Coordinates update checks and update actions |
startup-update.ts | Handles startup update behavior |
uninstall.ts | Removes managed install state |
state-preservation.ts | Preserves user-owned state during lifecycle operations |
version-resolver.ts | Resolves available and current versions |
Lifecycle code is security-sensitive because it can mutate install files and preserve or remove local state.
Agent evolution
The evolution subsystem supports reviewed improvement workflows. It handles candidate lifecycle, constraint gates, and export formats for evaluation or optimization data.
| Component | Role |
|---|---|
candidate-lifecycle.ts | Tracks candidate change lifecycle |
constraint-gate-runner.ts | Runs gate checks before promotion |
export-format.ts | Exports structured data for review or evaluation |
Evolution is designed to remain reviewable. Learned or generated behavior must not silently mutate live policy.
ACP server
src/acp/ contains the ACP server integration. It exposes editor and protocol integration separately from the normal CLI and channel surfaces.
Supporting infrastructure
These subsystems are smaller than the main runtime surfaces but still part of the architecture.
| Subsystem | Role |
|---|---|
src/theme/ | Terminal token resolution, skins, and plain-mode overlays |
src/knowledge/ | Code dependency graph and knowledge cache |
src/python-env/ | Managed Python capability environments |
src/capabilities/ | Optional capability setup and secret storage helpers |
src/workers/ | Python worker process |
src/search/ | Full-text query helpers |
src/reports/ | Model report rendering |
src/diagnostics/ | Provider and model diagnostic helpers |
Prompt architecture
Prompt assembly is layered. Key context groups include:
- Identity and profile guidance
- Safety and learned memory
- Project context, including
AGENTS.md - Session history
- Session recall and external recall
- Live user message and attachments
- Intent and skill instructions
- Skill setup and resources
- Workflow plan
- Tool menu
- Explicit reference context
- Tool results or continuation feedback
Cacheable layers, such as identity, safety, project context, skill resources, and compact skills index, are rebuilt only when underlying data changes. Non-cacheable layers, such as session history, recall, and live messages, are rebuilt every turn.
Semantic rules:
- Session-stable system context is preferred over mid-session mutation.
- Skills are progressively disclosed.
- Attachments are structured context, not fake user text.
- Channel-facing formatting happens after model generation.
AGENTS.mdis project context, not memory.- Recall and compression summaries are untrusted historical context.
Data flow
The primary direct-turn path:
- Input arrives from CLI or a channel
- Runtime normalizes message and attachments
- Prompt assembly builds a layered provider request
- Provider responds with text and/or tool calls
- Tool planner and executor run concrete actions under policy
- Continuation prompt feeds tool results back if needed
- Final output is formatted for the active surface
- Session, memory, approvals, trajectory, and related state are persisted
A turn can also attach to larger orchestration:
| Path | When it applies |
|---|---|
| Direct provider/tool loop | Normal chat and tool use |
| Delegation | Parent session spawns bounded child agent sessions |
| Workflow | Durable multi-step execution is started or resumed |
| Channel gateway | Remote surfaces route messages, progress, approvals, and final delivery |
AgentLoop.handle() coordinates the turn boundary. Provider iteration, tool planning, skill playbooks, child loop construction, and workflow execution live in specialized components.
v0.1.0 maturity matrix
| Area | Status | Notes |
|---|---|---|
| CLI | live-proven | Direct interaction surface. |
| Telegram | live-proven | First-party remote channel. |
| Discord | present-not-live-proven | Adapter code exists; live production use is not part of the v0.1.0 baseline. |
present-not-live-proven | Adapter code exists; live production use is not part of the v0.1.0 baseline. | |
operational-with-external-risk | QR-linked bridge, diagnostics, and setup flow exist; still gated because Baileys is an unofficial API. | |
| AgentLoop | live-proven | Core turn orchestration. |
| AgentLoopBuilder | live-proven | Session-scoped runtime assembly. |
| Provider execution | live-proven | Provider registry, executor, fallback, and auxiliary routes. |
| Tool execution | live-proven | Built-in tools and MCP tools. |
| Skill system | live-proven | Official, profile, and external skills. |
| Memory system | live-proven | Profile memory files and shared memory. |
| Security policy | live-proven | Adaptive mode with hardline floor. |
| Gateway | live-proven | Channel auth, routing, approvals, and delivery. |
| Setup and verification | live-proven | Onboarding, setup editor, and readiness checks. |
| Delegation | implemented | Bounded child sessions and subagent tracking. |
| Workflow | implemented | Durable multi-step execution with SQLite-backed state. |
| Packs | implemented | Pack validation, install, risk, and permission checks. |
| Lifecycle | implemented | Install, update, uninstall support, and state preservation. |
| Agent Evolution | implemented | Candidate lifecycle, gates, and export format. |
| Cron | implemented | Job scheduling and execution. |
| ACP | implemented | ACP server integration exists. |
| Knowledge graph | implemented | Code dependency graph and cache. |
| Browser, local CDP | live-proven | Local Chrome DevTools Protocol. |
| Browser, cloud | implemented | Browserbase is implemented behind explicit cloud-spend approval; other cloud browser providers remain stubs. |
| Web search | implemented | Brave Search and DDGS search are implemented. Guarded fetch extraction is implemented; crawl and other search providers remain stubs. |
| Eval runner | implemented | Deterministic fixtures exist. |
Architectural weak spots
create-runtime.tsremains the composition root - construction is explicit and reviewable, but the file is still large. Changes here should be narrow and heavily tested.- AgentLoop is decomposed but still central -
AgentLoopBuilder,ProviderTurnLoop,ToolPlanRunner,SkillPlaybookRunner, and child loop factories handle much of the work.AgentLoopstill coordinates turn boundaries, runtime events, continuation, cancellation, and persistence. - Gateway readiness vs. liveness -
estacoda gateway diagnosereports adapter readiness, not full background-process liveness. - Lifecycle and pack operations are high-impact - update, uninstall, pack install, and state-preservation paths can mutate local install or runtime state. Treat changes there as security-sensitive.
- Native SQLite bindings -
better-sqlite3requires install-time compilation on some platforms.
How to inspect
# Runtime readiness and config issues
estacoda model diagnose
# Full setup readiness
estacoda verify
# General diagnosis
estacoda doctor
# Gateway readiness per adapter
estacoda gateway diagnose
# Gateway full status
estacoda gateway status
# Provider state
estacoda model show
# Session list with surface attachments
estacoda sessions list
# Channel status
estacoda channels status
# Code dependency graph
estacoda knowledge code summary
estacoda knowledge code refresh