Entry Points & Execution Flow
Entry Points
OpenClaw has four distinct entry points:
1. CLI Entry (openclaw.mjs → src/entry.ts)
The primary user-facing entry point.
openclaw.mjs
│ (lightweight bootstrap — respawns Node with --disable-warning=ExperimentalWarning)
▼
src/entry.ts
│ 1. Set process.title = "openclaw"
│ 2. Install warning filter
│ 3. Normalize environment variables
│ 4. Check if experimental warnings need suppression
│ 5. If yes: respawn Node with the flag → parent exits
│ 6. If no: parse CLI profile args → apply profile env
│ 7. Dynamic import("./cli/run-main.js")
▼
src/cli/run-main.ts → runCli(argv)
│ Calls into the Commander.js program
▼
src/cli/program.ts → buildProgram()
│ Registers all subcommands from src/commands/
▼
Commander parses argv → dispatches to command handler
The respawn mechanism exists because --disable-warning=ExperimentalWarning cannot be set via NODE_OPTIONS — it must be a direct Node CLI flag. If not already set, the entry script spawns a child process with the flag and bridges stdin/stdout/signals.
2. Library Entry (src/index.ts)
For programmatic use (import { loadConfig } from "openclaw").
// src/index.ts — the library API
export {
loadConfig,
monitorWebChannel,
createDefaultDeps,
loadSessionStore, saveSessionStore,
resolveSessionKey, deriveSessionKey,
ensureBinary, ensurePortAvailable,
// ... utility exports
};Side effects on import:
loadDotEnv({ quiet: true })— loads .env filesnormalizeEnv()— standardizes environmentensureOpenClawCliOnPath()— adds CLI to PATHenableConsoleCapture()— wraps console.* into structured logsassertSupportedRuntime()— fails fast if Node is too old
If the module is the main entry (isMainModule), it also installs error handlers and calls program.parseAsync(argv).
3. Daemon Entry (src/cli/daemon-cli.ts)
For running as a background service.
openclaw daemon start
→ Spawns a detached process running daemon-cli.ts
→ Daemon starts the gateway server
→ Monitors channel health
→ Runs cron jobs
→ Accepts WebSocket connections from clients
4. Plugin SDK Entry (src/plugin-sdk/index.ts)
For third-party plugin development.
// External plugin code:
import { ... } from "openclaw/plugin-sdk";
export default async (api: OpenClawPluginApi) => {
api.registerTool(myTool);
api.on("beforePromptBuild", handler);
};Main Code Paths
Path 1: CLI Interactive Session
The most common path — user types openclaw and starts chatting.
buildProgram()
→ default command (no subcommand)
→ starts TUI (Terminal UI)
→ loads config
→ resolves default agent
→ creates agent session
→ enters interactive prompt loop:
user types message
→ runEmbeddedAttempt(params)
→ builds system prompt
→ creates tools
→ session.prompt(userMessage)
→ [agentic loop]
→ displays streamed response
→ waits for next input
Path 2: Gateway / Daemon Mode
For multi-channel operation.
openclaw daemon start (or openclaw gateway start)
→ starts HTTP + WebSocket server
→ loads config + plugins
→ starts channel monitors (WhatsApp, Telegram, etc.)
→ for each inbound message:
→ channel plugin normalizes message
→ gateway receives event
→ resolveRoute() → agent + session key
→ runEmbeddedAttempt(params)
→ [agentic loop]
→ dispatches response via channel outbound adapter
Path 3: Web Channel
For browser-based chat.
Browser connects to gateway WebSocket
→ talk.* server methods handle conversation
→ messages flow through same agent runner
→ responses streamed back via WebSocket
Path 4: Single Command Execution
For non-interactive use (openclaw exec "summarize this file").
openclaw exec <prompt>
→ single agent run
→ outputs response to stdout
→ exits
State Transitions
Agent Session Lifecycle
┌──────────┐
│ IDLE │
└────┬─────┘
│ inbound message
▼
┌──────────┐
┌─────│ LOADING │
│ └────┬─────┘
│ │ session loaded, prompt built
│ ▼
│ ┌──────────┐
│ │ RUNNING │ ◄─── session.prompt() active
│ └────┬─────┘
│ │
│ ┌────┴─────────────────────────┐
│ │ │
│ ▼ ▼
│ ┌──────────┐ ┌──────────┐
│ │ TOOL_USE │ │STREAMING │
│ └────┬─────┘ │ RESPONSE │
│ │ tool executes └────┬─────┘
│ │ result fed back │
│ └──► RUNNING ◄────────────────┘
│ │
│ │ no more tool_use
│ ▼
│ ┌──────────┐
│ │ COMPLETE │
│ └────┬─────┘
│ │ response dispatched
│ ▼
│ ┌──────────┐
└─────│ IDLE │
└──────────┘
Session Persistence States
NEW → first message creates session file
→ ACTIVE: session file + write lock held
→ IDLE: write lock released, file persists
→ COMPACTING: context window too large, auto-compaction triggered
→ REPAIRED: corrupt session file detected and fixed
Gateway Lifecycle
BOOT
→ Load config
→ Load plugins (discovery → manifest → import → init)
→ Set active plugin registry
→ Start HTTP + WebSocket server
→ Start channel monitors
→ Start cron scheduler
→ RUNNING
RUNNING
→ Config file changes detected → HOT_RELOAD
→ Plugin changes detected → PLUGIN_RELOAD
→ Channel health issues → CHANNEL_RESTART
HOT_RELOAD
→ Reload config
→ Refresh plugin registry
→ Restart affected channels
→ Reinitialize hook runners
→ RUNNING
SHUTDOWN
→ Stop channel monitors
→ Stop cron scheduler
→ Close WebSocket connections
→ Close HTTP server
Key State: The runEmbeddedAttempt() Function
This is the central orchestrator (located at src/agents/pi-embedded-runner/run/attempt.ts). A single invocation handles one complete agent run:
runEmbeddedAttempt(params)
1. Resolve workspace directory
2. Set up sandbox (if configured)
3. Load skill entries + apply env overrides
4. Resolve bootstrap context files (OPENCLAW.md, etc.)
5. Create tools (bash, read, write, glob, grep, web_search, message, etc.)
6. Build system prompt (identity + skills + memory + channel + runtime)
7. Open SessionManager (load/create session file)
8. Create SettingsManager
9. Call createAgentSession() from Pi SDK
10. Subscribe to session events (messages, tools, agent lifecycle)
11. Set up abort controller + timeout
12. Resolve images from prompt (if any)
13. Call session.prompt(effectivePrompt) ←── THIS DRIVES THE LOOP
14. Await completion (or abort/timeout)
15. Collect results: messages, usage, errors, tool metadata
16. Release session write lock
17. Return EmbeddedRunAttemptResult
Steps 1-12 are setup. Step 13 is where the actual AI execution happens — the Pi SDK takes over and runs the inner agentic loop (see The Agentic Loop). Steps 14-17 are teardown.