Skip to content

Dpaste20/MANA

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 

Repository files navigation

M.A.N.A

Multi-Agent Network Architecture

A terminal-native AI orchestration system. Route messages to multiple specialized AI agents simultaneously, receive their responses in real time, and manage the entire interaction from a single keyboard-driven interface — without leaving the terminal.

███╗   ███╗    █████╗    ███╗   ██╗    █████╗
████╗ ████║   ██╔══██╗   ████╗  ██║   ██╔══██╗
██╔████╔██║   ███████║   ██╔██╗ ██║   ███████║
██║╚██╔╝██║   ██╔══██║   ██║╚██╗██║   ██╔══██║
██║ ╚═╝ ██║██╗██║  ██║██╗██║ ╚████║██╗██║  ██║
╚═╝     ╚═╝╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝╚═╝╚═╝  ╚═╝

Overview

M.A.N.A is not a chatbot wrapper or a round-robin dispatcher. It is a structured orchestration layer that treats agents as peers with distinct identities and capabilities, routes messages to them with precision, and streams all of their responses concurrently into a purpose-built terminal UI. Watch the demo

Two codebases with a clean contract between them:

  • Python proxy server (mana-server/) — owns routing intelligence, fan-out, mention parsing, and upstream agent communication
  • Go TUI client (mana-tui/) — owns the terminal experience: streaming display, input handling, session management, status bar

Architecture

┌─────────────────────────────────────────┐
│           Go TUI Client                 │
│  Bubble Tea · Lip Gloss · Glamour       │
└───────────────┬─────────────────────────┘
                │  WebSocket  ws://localhost:8080/ws/chat
┌───────────────▼─────────────────────────┐
│         Python Proxy Server             │
│  FastAPI · asyncio · @mention parser    │
│  Fan-out queue · Heartbeat monitor      │
└──────┬──────────────┬───────────────────┘
       │              │              │
  ws://…:8000    ws://…:8004    ws://…:8003
       │              │              │
  ┌────▼────┐   ┌─────▼────┐   ┌────▼────┐
  │  Agent1 │   │  Agent2  │   │  Agent3 │
  └─────────┘   └──────────┘   └─────────┘

The agent registry is fully data-driven. All agents are declared in config.yaml. Adding a new agent requires no code changes — the proxy, TUI autocomplete, @mention parser, and /talk vocabulary all update automatically at startup.


Getting Started

Prerequisites

  • Python 3.11+ with pip
  • Go 1.21+
  • One or more agent backends exposing a WebSocket endpoint (see Agent Protocol)

1. Start the proxy server

cd mana-server
pip install -r requirements.txt
python server.py

The proxy listens on ws://0.0.0.0:8080/ws/chat by default.

2. Start the TUI client

cd mana-tui
go mod tidy
go run .

Connect to a different port with go run . --port 9090.

3. Register your agents

Edit mana-server/config.yaml — no code changes required anywhere else:

agents:
  myagent:
    display_name: MyAgent
    ws_url: ws://localhost:8001/ws/chat
    start_cmd: ".venv/bin/python server.py"   # optional — enables /wake
    work_dir: "~/Projects/MyAgent"            # optional — working directory

Routing

Single-agent pass-through

When a message targets exactly one agent, the proxy connects directly to that backend and pipes the stream to the client with no labeling overhead.

/talk airi
What are the key findings in this document?

Multi-agent fan-out

When a message targets multiple agents, the proxy spins up a concurrent AgentProxy task for each one. All tasks share a single asyncio.Queue. Chunks from every agent stream into the queue as they arrive and drain to the TUI in arrival order — fully interleaved, never blocked by the slowest agent.

/talk airi zephyr
Analyze this network configuration.

@mention message splitting

Inline @mentions split a single message into per-agent payloads before dispatch. Any text before the first mention becomes shared context, prepended to every agent's payload. Agent-specific text is delivered only to the intended recipient.

@Airi summarize this document and @Zephyr run a port scan on 192.168.1.1

@all broadcasts a chunk to every registered agent.

Mentions coexist with /talk state — a /talk airi session can still fire a one-off @zephyr query without changing the default routing target.


Commands

Command Description
/talk <agent> Route all subsequent messages to one agent
/talk <a1> <a2> Fan-out to multiple agents simultaneously
/talk all Broadcast to every registered agent
/online Show which agents are currently reachable
/online <agent> Check a specific agent's status
/wake <agent> Spawn an agent's server process and wait for it to come up
/wake <a1> <a2> Wake multiple agents in parallel
/wake all Wake every registered agent in parallel
/attach <path> Stage a .txt or .pdf file to send with the next message
/detach Clear all staged file attachments
/save-session [name] Serialize current conversation to ~/.mana/sessions/
/resume-session <name> Restore a saved conversation
/resume-session List all saved sessions
/help Show the command reference overlay

Keyboard shortcuts

Key Action
ctrl+f Open the interactive file picker
ctrl+y Copy the last agent response to clipboard
ctrl+d Clear all staged file attachments
↑ / ↓ Navigate input history or autocomplete suggestions
Tab Accept the highlighted autocomplete suggestion
Space Start / stop voice recording (when input is empty)
ctrl+c Quit

Terminal Interface

Per-agent response boxes

Each streaming agent response renders in its own labeled, rounded-border box. Boxes appear as streams arrive and grow in real time. When a stream completes, the box is committed to the message history. Multiple agent boxes render in the order their streams began — multi-agent conversations are structured and readable, not interleaved noise.

Status bar

A persistent bar at the bottom of the screen displays: connection status, active agent(s), voice state, message count, session ID, token count, generation time, and tokens per second. Transient notifications (file attached, session saved, agent switched) temporarily replace the bar for two seconds.

Autocomplete

The input field provides context-aware autocomplete for slash commands (triggered by /) and agent mentions (triggered by @). The suggestion list is rebuilt on every keystroke and always reflects the live registry state.

File picker

ctrl+f opens a full directory browser overlay. Navigate with arrow keys, open directories with Enter, attach .txt or .pdf files directly. Staged files appear as badges above the input field.

Supported types: .txt, .md, .log, .csv (plain text), .pdf (base64-encoded binary).

Voice input

Space initiates recording via arecord. A second Space stops recording, encodes the audio as base64, and sends it to the active agent(s). The agent returns a transcript chunk which the TUI renders as a user message before the response stream begins.


Agent Lifecycle

The proxy runs a background heartbeat that probes every registered agent's WebSocket URL every 10 seconds and updates AGENT_STATUS accordingly.

/wake acts on this state. The boot sequence per agent:

  1. Already online → reports status immediately, no spawn
  2. No start_cmd → emits a configuration error and stops
  3. Process already running → reports "still starting up"
  4. Otherwise → spawns via asyncio.create_subprocess_shell
  5. Polls the WebSocket URL every 500ms for up to 15 seconds
  6. Reports live progress every 3 seconds while waiting
  7. On success → updates AGENT_STATUS, reports boot time
  8. On timeout → emits a timeout error; the process may still be starting
┌─ Mana ──────────────────────────────────────────────┐
│ Waking 3 agents in parallel...                      │
│                                                     │
│ • Airi is already online 🟢                         │
│ • Waking Zephyr...                                  │
│   Still waiting... (3s)                             │
│ • Zephyr is online 🟢  (boot time: 4.2s)            │
│ • ITAA is online 🟢  (boot time: 2.1s)              │
└─────────────────────────────────────────────────────┘

Agent Protocol

Any backend that can speak WebSocket integrates with M.A.N.A. The required contract:

Incoming payload (JSON):

{
  "message": "user message text",
  "session_id": "terminal-abc123",
  "files": [
    { "name": "doc.pdf", "content": "<base64>", "mime_type": "application/pdf" }
  ]
}

Outgoing message types:

Type Fields Description
start Optional. Signals stream begin.
chunk content: string One chunk of the streaming response.
end token_count: int, generation_time: float Signals stream completion.
error message: string An error occurred; stream will not continue.

Any agent framework that can emit these message types over WebSocket — agno, LangChain, LlamaIndex, a raw FastAPI server, a custom model endpoint — integrates with M.A.N.A out of the box.


Session Persistence

Conversations are serialized to ~/.mana/sessions/<name>.json and can be fully restored across terminal sessions. Session names are slugified on save. A failed /resume-session lookup performs fuzzy matching and suggests the closest existing name.


Error Handling

Agent failures are isolated. If a connection is refused, the proxy emits an Agent offline error chunk into the shared queue. If a connection drops mid-stream, the error is caught, surfaced inline, and the queue sentinel is pushed — the fan-out drain loop continues normally for all other agents. A dead agent never hangs the interface.


Tech Stack

Layer Technology
Proxy / Orchestration FastAPI, Python, asyncio
Agent Communication WebSocket (websockets)
Routing & Fan-out Custom async queue, @mention parser
TUI Frontend Go, Bubble Tea, Lip Gloss, Glamour
Input / Viewport Charmbracelet Bubbles
Agent Registry config.yaml (data-driven)
Agent Lifecycle asyncio.create_subprocess_shell, WebSocket polling
Session Persistence JSON at ~/.mana/sessions/
File Attachments Base64-encoded, multi-format
Voice Input arecord, base64 audio
Clipboard atotto/clipboard

Design Principles

Agents are peers, not tools. Every registered agent has a name, an identity, and a dedicated response surface in the UI. Multi-agent interaction is a conversation with a team, not a query dispatched to an abstraction.

Routing intelligence lives in the proxy. The TUI knows which agents the user has selected. The proxy knows how to split, label, and fan-out messages. Agent backends know nothing about each other.

Streaming is first-class. No response is buffered and delivered whole. Every chunk arrives at the terminal the moment the agent produces it — even in fan-out mode with multiple concurrent streams.

The registry is the source of truth. Agent identity, routing targets, autocomplete vocabulary, heartbeat monitoring, and /talk resolution all derive from config.yaml. There is no hardcoded agent list anywhere in the runtime path.

Failures are isolated, never cascading. An offline agent produces an inline error and a sentinel. It does not stall the queue, hang the drain loop, or affect the streams of reachable agents.

Terminal aesthetics are non-negotiable. The TUI is not a concession to the absence of a GUI. It is the intended interface — purpose-built with rounded borders, color-coded agent boxes, a live status bar, smooth streaming, and keyboard-first interaction.


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors