Architecture

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.

workspace layout
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 point
dependency graph
core  ←──  llm  ←──  agent  ←──  server
  ↑          ↑          ↑           ↑
  └── tools ─┴── mcp ───┴── tui ────┘
       ↑          └── persistence ──┘
       └── memory ──┘  (depends on core + llm)
crates/core

The foundation — depends on nothing. IDs (UUID v7), messages & events, findings, AppConfig, export (PDF/HTML/JSON/DOCX), notifications, CRM sync, exact token counting.

crates/llm

Provider abstraction. LlmProvider trait with complete() and stream(), retry with exponential backoff, streaming fallback for large responses, response size limits.

crates/agent

The runtime itself: agent loop, coordinator, compaction, prompt builder, tool executor, result budgets, control plane, IPC, process manager, doom-loop detector, hooks, session resume.

crates/tools

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.

crates/memory

Long-term semantic memory + entity graph (mem0 / Memora model): facts, FTS5, embeddings, 5-outcome absorb pipeline, hybrid search, distillation, secret detection.

crates/mcp

Model Context Protocol. Client: stdio + Streamable HTTP transports, OAuth client-credentials, dynamic tool discovery. Server: mcp-serve exposes every tool and executes tools/call.

crates/persistence

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.

crates/server

Axum HTTP API: REST endpoints for sessions, agents, jobs and memory; control-plane answers/approvals; SSE streaming; API-key auth, rate limiting, Prometheus metrics.

crates/tui

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.

01
Build the system prompt

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.

02
Check the doom loop

DoomLoopDetector inspects recent history before another token is spent: three identical tool calls in a row stops the loop.

03
Estimate tokens → compact

Real BPE counting with tiktoken cl100k_base (CJK-aware heuristic fallback). When estimated tokens reach context_window × compact_threshold (default 0.50), compaction runs.

04
Call the LLM — streaming

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.

05
Pass the gates

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.

06
Execute the batch

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.

07
Loop

Repeat until the model stops issuing tool calls or max_iterations is reached.

08
Return

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.

01
Plan

The LLM decomposes the query into sub-tasks; sub-tasks are persisted to the database before anything spawns.

02
Fan-out

Sub-agents start as a JoinSet of tokio tasks — or as OS processes via ProcessManager when use_multiprocess = true.

03
Collect

Results come back as budget-capped summaries, keeping the coordinator context small enough to reason over the fleet.

04
Reflection

For lead generation, count-based: if the contact quota is missed, gap rounds run until the target is met.

05
Goal Mode — LLM judge

A judge compares the result against the original goal; concrete gaps become new gap-filling sub-tasks, up to replan_rounds rounds.

06
Synthesize

The LLM merges findings into a single report: index.md, summary.md and the findings/ directory.

07
Export + Notify

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.

unix socket IPC
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 modeDefense
Transient LLM failureRetry with exponential backoff (3 attempts), Retry-After respected
Looping agentDoom-loop detection: three identical tool calls → stop
Failed tool callCascading cancellation: a shell failure cancels sibling calls; cancellation tokens propagate through the whole agent tree
Destructive commandGuard blocks rm -rf /, mkfs, fork bombs before execution
Tool errorGraceful degradation: errors return to the model as tool results so it can adapt