Architecture
Parallel Research is a modular system of 9 crates in a Cargo workspace, built around an asynchronous agent loop on tokio. This page covers the crate map, the agent lifecycle, the coordinator's fan-out with Goal Mode, multi-process isolation, context management and error handling.
Crate Map
The workspace layout. core depends on nothing; agent ties llm, tools, memory and persistence together; server and tui are the two outer shells.
parallel-research/
├── crates/
│ ├── core/ # fundamental types & domain logic
│ ├── llm/ # LLM provider abstraction
│ ├── agent/ # agent runtime, coordination, control plane
│ ├── tools/ # 44 tools (web, osint, memory, question...)
│ ├── memory/ # long-term semantic memory + entity graph
│ ├── mcp/ # Model Context Protocol (client and server)
│ ├── persistence/ # storage (SQLite, connection pool, jobs)
│ ├── server/ # HTTP API
│ └── tui/ # terminal interface
└── src/main.rs # CLI entry pointcore ←── llm ←── agent ←── server
↑ ↑ ↑ ↑
└── tools ─┴── mcp ───┴── tui ────┘
↑ └── persistence ──┘
└── memory ──┘ (depends on core + llm)The foundation — depends on nothing. IDs (UUID v7), messages & events, findings, AppConfig, export (PDF/HTML/JSON/DOCX), notifications, CRM sync, exact token counting.
Provider abstraction. LlmProvider trait with complete() and stream(), retry with exponential backoff, streaming fallback for large responses, response size limits.
The runtime itself: agent loop, coordinator, compaction, prompt builder, tool executor, result budgets, control plane, IPC, process manager, doom-loop detector, hooks, session resume.
44 tools (+5 browser): web, files, exec, vision, git, OSINT, verification, enrichment, memory, control plane. Plus registry, 7-backend search engine, SSRF guard, prompt-injection detection.
Long-term semantic memory + entity graph (mem0 / Memora model): facts, FTS5, embeddings, 5-outcome absorb pipeline, hybrid search, distillation, secret detection.
Model Context Protocol. Client: stdio + Streamable HTTP transports, OAuth client-credentials, dynamic tool discovery. Server: mcp-serve exposes every tool and executes tools/call.
SQLite in WAL mode with a 4-connection pool: sessions, agents, messages, findings, sub-tasks. Contact DB, durable jobs with self-healing retry, session history.
Axum HTTP API: REST endpoints for sessions, agents, jobs and memory; control-plane answers/approvals; SSE streaming; API-key auth, rate limiting, Prometheus metrics.
Ratatui interface: multi-agent tree view, live streaming buffer, jobs and memory panels, operator control — answer questions, grant approvals, watch the fleet.
Agent Lifecycle
Every agent — top-level or spawned — runs the same eight-step loop. The loop is where caching, safety gates and budget discipline live.
PromptBuilder stacks three cache tiers — stable (identity, role, task), context (cwd, platform, model, date), volatile (tools, skills, memory). Depth-0 agents additionally receive the memory digest as a frozen snapshot, keeping the prefix cache warm.
DoomLoopDetector inspects recent history before another token is spent: three identical tool calls in a row stops the loop.
Real BPE counting with tiktoken cl100k_base (CJK-aware heuristic fallback). When estimated tokens reach context_window × compact_threshold (default 0.50), compaction runs.
Text deltas hit the UI live as events; tool calls are assembled from stream fragments. On stream failure the runtime falls back to a non-streaming complete() automatically.
Role-based deny → approval flow for approval_tools (y/n in the TUI, POST /sessions/:id/approve via API) → PreToolUse subprocess hooks that can allow, deny or enrich.
Read-only calls run in parallel, writes serialize. question goes to the operator, spawn_agent starts children, discovered contacts are autosaved and absorbed into memory, results are truncated and appended.
Repeat until the model stops issuing tool calls or max_iterations is reached.
The final answer returns to the caller. At coordinator level this is where findings get synthesized into the report.
Coordinator Flow & Goal Mode
A research run is the same loop one level up: the Coordinator plans, fans out, collects under budgets, lets an LLM judge check the result against the goal, then synthesizes and ships.
The LLM decomposes the query into sub-tasks; sub-tasks are persisted to the database before anything spawns.
Sub-agents start as a JoinSet of tokio tasks — or as OS processes via ProcessManager when use_multiprocess = true.
Results come back as budget-capped summaries, keeping the coordinator context small enough to reason over the fleet.
For lead generation, count-based: if the contact quota is missed, gap rounds run until the target is met.
A judge compares the result against the original goal; concrete gaps become new gap-filling sub-tasks, up to replan_rounds rounds.
The LLM merges findings into a single report: index.md, summary.md and the findings/ directory.
Results are absorbed into long-term memory, exported to PDF/HTML/JSON/DOCX, and announced via webhook, email or Telegram.
Multi-Process
By default all agents run in one process as tokio tasks. With use_multiprocess = true, each sub-agent becomes its own OS process (parallel-research worker …) talking to the coordinator over Unix domain sockets with JSON-line messages.
Coordinator (process 1)
│ Unix socket IPC (JSON lines)
├──spawn──► Worker (process 2) ── agent_id_1
├──spawn──► Worker (process 3) ── agent_id_2
└──spawn──► Worker (process 4) ── agent_id_3
events: progress · tool-call · completion- kill_on_drop — worker handles are spawned with
kill_on_drop(true): a dropped handle terminates the child, so no orphaned processes survive a cancelled run. - 30 s startup timeout — a worker must open its socket within 30 seconds or it is reaped and reported.
- SQLite WAL mode — concurrent access from every process against one database, pool of 4 connections.
- Isolation — one agent crashing never takes down the fleet; resource limits apply per process.
Context Management
Five stages keep the context window honest. Compaction triggers when estimated_tokens ≥ context_window × compact_threshold (default 0.50). Long-term memory complements all of it: relevant facts arrive as a prompt digest instead of living in session context.
BPE token counting
Exact counting with tiktoken cl100k_base; CJK-aware heuristic fallback. You cannot manage what you estimate wrong.
Tool result truncation
50 KB / 2000 lines per tool output, 200 KB per-turn budget. Overflow is persisted to disk with a pointer, so nothing is lost.
Micro-compaction
Deduplicate tool results by hash and prune old outputs — fast, deterministic, no LLM call.
Full compaction
Hermes-style: an LLM summarizes the middle of the conversation; head + summary + tail remain in context.
Anti-thrashing cooldown
After an ineffective compression pass, the runtime backs off instead of compacting again and again.
Error Handling
Failure modes are designed for, not patched onto the happy path:
| Failure mode | Defense |
|---|---|
| Transient LLM failure | Retry with exponential backoff (3 attempts), Retry-After respected |
| Looping agent | Doom-loop detection: three identical tool calls → stop |
| Failed tool call | Cascading cancellation: a shell failure cancels sibling calls; cancellation tokens propagate through the whole agent tree |
| Destructive command | Guard blocks rm -rf /, mkfs, fork bombs before execution |
| Tool error | Graceful degradation: errors return to the model as tool results so it can adapt |