Source tree and reading order

Corresponding source

Reading layerDirectory or file
Python product codenanobot/
Agent corenanobot/agent/loop.py, runner.py, context.py, tools/
Runtime surfacesnanobot/cli/, api/, sdk/, channels/, gateway/, webui/
Frontend and Bridgewebui/, bridge/
Executable behavior specificationstests/
Official usage documentationdocs/
Packaging entrypyproject.toml, hatch_build.py

Learning objectives

After this lesson, you should be able to:

  • explain the responsibilities of the main directories;
  • map user-visible behavior to the responsible module;
  • read by call chain instead of mechanically traversing files;
  • distinguish the core, integration, and presentation layers.

Prerequisites

Project root

PathResponsibilityBeginner priority
nanobot/Python runtimeHigh
tests/Python behavior and regression testsHigh
docs/Current repository usage and development documentationHigh
webui/React/TypeScript WebUILater
bridge/Integrations requiring a separate bridge serviceOptional
pyproject.tomlpackage, dependencies, CLI entry point, plugin entry pointsMedium
.agent/design, security, and common pitfallsHigh

Map of nanobot/

DirectoryPrimary responsibilityRead first
agent/Turn, Runner, Context, Memory, Tools, Subagentloop.py, runner.py, context.py
bus/inbound/outbound queues and runtime eventsevents.py, queue.py, runtime_events.py
session/Session JSONL, keys, goals, continuations, WebUI turn coordinationmanager.py, keys.py
providers/LLM Provider contracts, registration, creation, fallback, adaptersbase.py, registry.py, factory.py
channels/external chat platforms and the WebSocket adapterbase.py, registry.py, manager.py
config/Pydantic schema, loading, pathsschema.py, loader.py
cli/Typer CLI and the main runtime assembly entry pointcommands.py
api/OpenAI-compatible HTTP APIserver.py
sdk/SDK types, streams, and runtime helpersenter through nanobot/nanobot.py
cron/workspace-scoped scheduling, bound Turns, deliveryservice.py, bound_runner.py
command/slash-command routing and built-in commandsrouter.py, builtin.py
pairing/pairing approval for unauthorized direct-message sendersstore.py
security/workspace and network boundariesworkspace_policy.py, network.py
webui/Python-side WebUI API, tokens, threads, and media coordinationchoose by problem
apps/App protocol and CLI app servicedo not confuse with Skill or Tool
audio/transcription Provider registration and dispatchOptional
skills/built-in Skill filesstart with README.md
templates/system prompt, bootstrap, Dream, and Heartbeat templatesread while studying context

Core and edge layers

The project design keeps the core small and places capabilities at the edges:

flowchart TD
    CORE["Core path<br/>AgentLoop + AgentRunner"]
    CTX["Context / Session / Provider contracts"] --> CORE
    CORE --> TOOLS["Tools"]
    CORE --> EVENTS["General runtime events"]
    CH["Channels"] --> CORE
    MCP["MCP"] --> TOOLS
    SK["Skills"] --> CTX
    EVENTS --> UI["WebUI / WebSocket coordination"]
    CRON["Cron / Heartbeat"] --> CORE

Design implications:

  • prefer Tool, Skill, MCP, or Channel for a new capability;
  • put behavior in AgentLoop/Runner only when it is general Turn behavior that cannot be expressed at the edge;
  • keep UI wire details out of AgentLoop;
  • allow limited duplication in Providers and Channels instead of forcing a difficult universal base class.

Source evidence:

  • .agent/design.md

Pass 1: Read only the skeleton

  1. nanobot/bus/events.py
  2. nanobot/bus/queue.py
  3. nanobot/agent/loop.py::TurnState
  4. AgentLoop.process_direct
  5. AgentLoop._process_message
  6. AgentRunner.run
  7. AgentRunner._run_core

Goal: draw only the primary call chain. Do not enter every helper.

Pass 2: Add context and state

  1. nanobot/agent/context.py
  2. nanobot/session/manager.py
  3. nanobot/session/keys.py
  4. nanobot/agent/memory.py
  5. nanobot/bus/runtime_events.py

Goal: answer “What did the model see, what was saved to disk, and what did the UI receive?”

Pass 3: Add Provider and Tool

  1. nanobot/providers/base.py
  2. nanobot/providers/factory.py
  3. nanobot/providers/registry.py
  4. nanobot/agent/tools/base.py
  5. nanobot/agent/tools/registry.py
  6. nanobot/agent/tools/loader.py

Goal: answer “How is the model selected, how are tools discovered, how are arguments validated, and how are results reinserted?”

Pass 4: Choose one runtime surface

Select a path:

  • CLI: nanobot/cli/commands.py
  • API: nanobot/api/server.py
  • SDK: nanobot/nanobot.py
  • Channel: nanobot/channels/base.py → manager.py → one concrete Channel
  • WebUI: nanobot/channels/websocket.py → session/webui_turns.py → Python WebUI API → React

Pass 5: Advanced mechanisms

  • agent/autocompact.py
  • agent/memory.py
  • session/turn_continuation.py
  • session/goal_state.py
  • agent/subagent.py
  • cron/
  • security/

Test reading order

Do not start by running the full test suite. Choose the file closest to the subject:

SubjectTest starting point
Loop/Runner main pathtests/agent/test_loop_runner_integration.py
Runner basic branchestests/agent/test_runner_core.py
Tool executiontests/agent/test_runner_tool_execution.py
Errors and safetytests/agent/test_runner_errors.py, test_runner_safety.py
Contexttests/agent/test_context_builder.py
Sessiontests/agent/test_session_manager_history.py, test_session_atomic.py
Runtime eventstests/bus/test_runtime_events.py
Providertests/providers/
Channeltests/channels/test_base_channel.py, test_channel_plugins.py
Subagenttests/agent/test_subagent_lifecycle.py

Locate a directory from a symptom

SymptomInspect first
wrong model or keyconfig/, providers/
Tool schema or argument erroragent/tools/, agent/runner.py
broken history ordering in one conversationagent/loop.py, session/
Channel does not receive or sendchannels/, pairing/
UI state does not refreshbus/runtime_events.py, session/webui_turns.py, channels/websocket.py
Cron does not runcron/, Gateway assembly, workspace cron store
filesystem access escapes its boundaryagent/tools/filesystem.py, security/
HTTP tool reaches a private networksecurity/network.py, Web/MCP callers
prompt behavior changesagent/context.py, templates/, Skills, Session history

Common reading mistakes

Mistake 1: Inferring responsibility from a filename

gateway/runtime.py sounds like the entire Gateway runtime, but the main service assembly path is cli/commands.py::_run_gateway.

Mistake 2: Treating tests as a substitute for implementation

Tests prove protected behavior, but they do not replace the actual call chain. Verify the entry point and boundary that each test covers.

Mistake 3: Assuming every entry point is identical because code is shared

API, SDK, and CLI can all call process_direct(), but their session keys, stream adapters, locks, and error formats differ.

Mistake 4: Forgetting the shared contract after entering one Provider

Understand LLMProvider and LLMResponse before reading the OpenAI-compatible adapter used by DeepSeek.

Self-check

  1. If WebUI emits an incorrect _turn_end, which modules should you inspect first?
  2. If a new capability only teaches the model how to complete a task, why should you consider a Skill first?
  3. If the API returns 400 while CLI works, why should you begin with api/server.py?
  4. Why should the bar for changing AgentLoop/Runner be higher than for adding a Tool?

Next step

Read the minimal troubleshooting flow, then continue to One Turn.