# Architecture Overview

## Module Dependency Graph

Thirteen runtime modules on one lockstep version. Their Maven runtime
dependencies form four layers, plus a BOM that pins the runtime artifacts.

![Module Dependency Graph: TnsAI 13 runtime modules across Foundation / Core-backed / Composed / Application + BOM meta layer](https://tnsai.dev/assets/diagrams/architecture-modules.svg)

<details>
<summary>ASCII fallback (for terminal / plain-text readers)</summary>

```
FOUNDATION
  tnsai-core            Agent · Role · Action · Events · SPI · ToolMethodRegistry
                        (no inter-module dependencies)
       │
       ▼
CORE-BACKED — depend on tnsai-core and no other runtime module
  tnsai-llm             LLMClient impls for 31 providers · prompt caching
  tnsai-coordination    Group topologies · council · voting
  tnsai-mcp             Model Context Protocol client + server
  tnsai-channels        Telegram · CLI · Email · Slack · Discord · WhatsApp
                        adapters via adapter SPI
  tnsai-payments        x402 payments · wallet SPI · liability records
  tnsai-integration     SCOPBridge + framework adapters
  tnsai-rag             Retrieval strategies · document chunking ·
                        content extractors
       │
       ▼
COMPOSED — add one or more runtime module dependencies beyond core
  tnsai-intelligence    → core, coordination (runtime scope)
                          Planning · reasoning · context
  tnsai-quality         → core, llm
                          Observability · security enforcement
  tnsai-evaluation      → core, quality
                          Benchmarks · quality gates · evaluators
  tnsai-tools           → core, quality, mcp
                          63 POJO toolkits · 209 @Tool methods · 29 categories
       │
       ▼
APPLICATION
  tnsai-server          → core, llm, coordination (runtime scope), quality, rag
                          WebSocket backend · RAG service · tool execution

META
  tnsai-bom             Bill of Materials — pins all 13 runtime modules
                        to one version
```

</details>

## Core Concepts

### Agent Lifecycle

![Agent lifecycle: a user message flows through agent.chat into Memory, System Prompt, and LLM call. The LLM response branches into either a text reply (returned to the user) or a tool call routed to ActionExecutor. ActionExecutor dispatches one of four ActionType handlers (LOCAL, WEB_SERVICE, LLM, MCP_TOOL), and the tool result loops back to the LLM for multi-turn execution.](https://tnsai.dev/assets/diagrams/agent-lifecycle.svg)

<details>
<summary>ASCII fallback (for terminal / plain-text readers)</summary>

```
User Message
    │
    ▼
agent.chat(message)
    │
    ├──  Memory         append to conversation history
    ├──  System Prompt  identity + roles + invariants + state
    └──  LLM call       request with tool definitions
                                │
                                ▼
                        LLM Response
                                │
                ┌───────────────┴───────────────┐
                │                               │
                ▼                               ▼
            Text reply                      Tool Call
                │                               │
                │                               ▼
                │                         ActionExecutor
                │                               │
                │                               │   ActionType:
                │                               ├──  LOCAL        Java method on Role
                │                               ├──  WEB_SERVICE  HTTP / REST endpoint
                │                               ├──  LLM          LLM dispatch via ToolMethodDispatcher
                │                               └──  MCP_TOOL     Model Context Protocol tool
                │                               │
                │                               ▼
                │                         Tool Result
                │                               │
                │                               ▼
                │                     back to LLM (multi-turn loop)
                │
                ▼
          return to user
```

</details>

### Action Routing

Actions are discovered from Roles via `@ActionSpec` annotations. The `ActionExecutor` routes each action to the correct executor based on `ActionType`:

| Type | Source | Example |
|------|--------|---------|
| `LOCAL` | Java method on Role | `@ActionSpec(type = ActionType.LOCAL) String greet(String name)` |
| `WEB_SERVICE` | HTTP / REST endpoint | `@ActionSpec(type = ActionType.WEB_SERVICE) + @WebService(...)` |
| `LLM` | LLM dispatch using the agent's `ToolMethodDispatcher` | `@ActionSpec(type = ActionType.LLM)` + agent-level `.builtInTools(...)` / `.toolPojos(...)` |
| `MCP_TOOL` | Model Context Protocol server tool | `@ActionSpec(type = ActionType.MCP_TOOL) + @MCPTool(serverUrl = "...")` |

Which of `@ActionSpec`, `@Tool`, and `BuiltInTool` to reach for is a
separate decision. See Actions vs Tools.

### Extension Points (SPI)

TnsAI uses Java's `ServiceLoader` pattern for modular extensibility.
TnsAI 0.14.0 (`TnsAI@b021c635`, TAN-2903) caches most provider
lookups through `com.tnsai.spi.SpiLoader`. See
[SPI Reference](../reference/spi.md). `0.14.1` uses
that cached loader, while `0.13.0` called `ServiceLoader.load()` on every lookup.

| SPI Interface | Module | Purpose |
|---------------|--------|---------|
| `LLMClientProvider` | Core | Register LLM providers |
| `CheckpointerProvider` | Core | State persistence |
| `PlannerHandle.Factory` | Intelligence | Planning algorithms |
| `ReasoningStrategyHandle.Factory` | Intelligence | Reasoning patterns |
| `ContextManagerHandle.Factory` | Intelligence | Decision tracing |
| `EvalHandle.Factory` | Quality | Evaluation hooks |
| `SecurityEnforcerHandle.Factory` | Quality | Security policies |

Register implementations in `META-INF/services/<interface-name>`.

### Agent Internal Architecture

The `Agent` facade composes a small set of focused collaborators. Each
owns one slice of behavior, so extending or replacing one piece doesn't
ripple through the whole class. If you're subclassing `Agent` or
swapping out one of these collaborators via SPI, this is the map.

![Agent internal architecture: the Agent facade composes focused collaborators: AgentOrchestrator (chat loop, tool-call routing, KB integration), AgentStreamingSupport (streaming chat + ChatChunk events), AgentCapabilities (planning, reasoning, eval, feedback, environment, variant, resilience), AgentCognitiveSupport, AgentHierarchyManager (parent/child relationships), AgentMessagingHandler (inter-agent messaging via communication SPI), AgentGroupManager (group membership).](https://tnsai.dev/assets/diagrams/agent-internal.svg)

<details>
<summary>ASCII fallback (for terminal / plain-text readers)</summary>

```
Agent                       identity · lifecycle · template methods · facade
   │
   ├── AgentOrchestrator    chat · streaming hooks · tool-call loop · KB
   │
   ├── AgentCapabilities    planning · reasoning · eval · feedback ·
   │                        environment · variant · resilience
   │
   ├── AgentCognitiveSupport
   │                        internal cognitive support (public for visibility)
   │
   ├── AgentHierarchyManager
   │                        parent / child relationships
   │
   ├── AgentStreamingSupport
   │                        streaming chat + ChatChunk events
   │
   ├── AgentMessagingHandler
   │                        inter-agent messaging via communication SPI
   │
   └── AgentGroupManager    group membership
```

</details>

### What runs on a chat turn

`agent.chat(message)` uses the conversation history and the agent's tools. On each turn, `AgentOrchestrator`:

1. Fails if the agent has no LLM client, then notifies the before-chat listeners.
2. Adds the message to memory, and prunes memory to the token limit when one is set.
3. Calls the model with the history, the tools and any retrieved knowledge. When a reasoning strategy is enabled, it runs that strategy instead.
4. Runs the tools if the reply asks for them. Otherwise it stores the reply in memory.
5. Notifies the after-chat listeners and records a decision trace when a context graph is on. A failed turn is traced too.

For the streaming and event-based variants of a turn, see [Streaming](../agents/streaming.md).

## Design Principles

1. **Annotation-first**: `@ActionSpec` and `@AgentSpec` over programmatic config, and channels are `ChannelAdapter` implementations
2. **SPI for extensibility**: modules register via `META-INF/services/`
3. **Composition over inheritance**: Agent delegates to focused managers
4. **Immutability**: records for data, `List.of()`, `Map.of()`
5. **Thread safety**: `ConcurrentHashMap`, `CopyOnWriteArrayList`, virtual threads
