Browser / Native App
│ HTTP + WebSocket (SSE compatibility route retained)
▼
FastAPI Controller (services/controller/fastapi_app/)
│ routers → api/ business logic
▼
Worker Adapters (integrations/workers/)
│ stdio subprocess, local HTTP, or vendor HTTPS SDK
▼
Worker processes: Jaeger AI · Hermes · Claude Code · Codex · Ollama · cloud APIs
Three layers, one product:
| Layer | Owns |
|---|---|
Client (apps/web/, apps/macos/) |
Presentation, navigation, user interaction |
Controller (services/controller/) |
Sessions, identity, auth, context assembly, streaming |
Workers (integrations/workers/) |
Model inference, tool execution, code loops |
The primary browser chat path is intentionally direct today:
POST /api/chat/start → RealtimeService.start_chat
→ framework adapter selected for the session
→ worker/provider implementation
→ run journal + WebSocket observation
The SI planner, evaluator, and response composer exist, but they are not in this
path. Scheduled plan resumption reaches collected execution through
DispatchService, which now resolves the same framework adapters and does not
silently fall back to the legacy registry. The compatibility registry injection
remains only for older unit tests while migration completes. See
ADR-0009.
The intended dispatch pipeline is planner → orchestrator → worker. The sequence below is a target design, not current primary-chat behavior:
User message → classify intent → pick worker → send briefing → get result → verify → respond
User message → create plan → execute step 1 → verify → execute step 2 → verify → ... → synthesize → respond
Independent steps run concurrently. Dependent steps run sequentially. Concurrency limits are configurable.
Paperclip is a functional multi-agent framework. From its source (SPEC-implementation.md, PRODUCT.md, docs/start/architecture.md), its core loop is:
Board defines goals → org tree of agents → heartbeat scheduler fires
→ adapter execute() spawns agent → agent works via REST API
→ result + cost captured → run recorded → budget checked → audit logged
Comparing against ARES’s actual code (core/si/), first principles:
| Paperclip capability | ARES equivalent (verified in code) | Status |
|---|---|---|
| Task hierarchy → goal | planner.py Plan/Step model with dependencies |
✅ exists |
| Task assignment | planner.assign_workers, worker_registry.find_eligible |
✅ exists |
| Agent registry + capabilities | worker_registry.py (register, find_by_capability, availability) |
✅ exists |
| Adapters (any runtime) | integrations/workers/ + ReasoningProvider protocol |
✅ exists |
| Approval gates | core/authority/route_approvals.py, os_automation_consent.py, trust_engine approval checks |
✅ exists |
| Audit trail | core/events/turn_journal.py, run_journal.py, disclosure ledger |
✅ exists |
| Verification of results | evaluator.py (6 checks) + response_composer.py |
✅ exists |
| Heartbeat scheduler | api/schedule_scheduler.py, schedules_store.py |
⚠️ exists but not wired to plans/runs |
| Run records + session resume across runs | run_journal.py + session lifecycle |
⚠️ partial |
| Budget enforcement (hard stop) | none | ❌ missing |
| Board/org chart/companies | — | N/A by design (one assistant, no org — see vision.md) |
Current assessment: ARES has many of the required primitives, but does not yet provide Paperclip-equivalent unattended execution. The missing loop is the heartbeat → run → budget cycle:
run_journalThat closes the parity gap without copying Paperclip’s org chart. Steal the engine, skip the company (per vision.md).
The dispatch path ARES is building, shaped by the Claude Code and Hermes source research:
Agent tool and Hermes delegate_task. The model decides when to delegate; the system executes. No separate dispatcher service.LocalAgentTask pattern).Reference: claude-code-dispatch-research.md (GitHub + analysis folders), ares-jaeger-lessons.md.
This is the target verification graph. The modules exist, but the graph is not yet applied to every primary-chat response:
WorkerResult.core/si/evaluator.py runs 6 checks: not-empty, reasonable length, no secret leak, no harmful content, code syntax, factuality markers.core/authority/route_approvals.py gates before the result is applied.core/si/response_composer.py merges verified results into one coherent response with provenance.core/events/turn_journal.py / run_journal.py — the audit trail.This mirrors LangGraph’s graph-of-nodes model: each node (producer, verifier, synthesizer) runs, passes state along edges, and conditional edges route failures to retry or human escalation. ARES’s implementation is the planner/orchestrator/evaluator in core/si/, not a new runtime.
ARES is building a hard boundary between the model proposes and the system acts. Individual approval and trust controls exist; universal enforcement through the unwired SI graph must not yet be assumed:
| Class | Who can see | Rule |
|---|---|---|
| Public | Any worker | Include freely |
| Personal | Approved providers | Include with disclosure tracking |
| Private | Local workers only | Redact from cloud briefings |
| Sensitive | Explicit approval per task | Redact by default |
| Secret | Never leaves device | Never include in any briefing |
ARES_HOME/ ARES-owned state (read + WRITE)
Worker stores Worker-owned state (read ONLY)
ARES never writes another app’s store. When a worker session needs a new turn, ARES asks the worker to write it. This is a deliberate boundary.
| Type | What | Lifecycle |
|---|---|---|
| Episodic | Conversations, events, actions | Permanent, searchable |
| Semantic | Facts, preferences, decisions | Permanent, searchable |
| Working | Current task state, active plan | Cleared when task completes |
| Scratchpad | Worker temporary state | Cleared when worker task completes |
Workers never see the full journal. The context compiler assembles a filtered, token-budgeted briefing per task: identity, relevant user context, project context, recent conversation, relevant memories, constraints, privacy policy, available tools, and output requirements.
| Subsystem | Owns |
|---|---|
| Identity | Name, persona, behavioral principles |
| Memory | Journal, user model, preferences, decisions |
| Context | What to send, what to redact, what budget |
| Trust | Data classification, provider eligibility, approval gates |
| Planning | Task decomposition, step ordering, dependencies |
| Routing | Which worker for which task |
| Verification | Checking worker output against expectations |
| Response | Final voice, uncertainty framing, activity summary |
| Policy | What requires approval, what data can leave the device |
Workers own execution. They do NOT own identity, memory, policy, or the user relationship.
| Worker | Owns |
|---|---|
| Hermes | Tool execution, terminal ops, file ops, agent loops |
| Claude Code | Code generation, reasoning |
| Codex | Code generation, general reasoning |
| Ollama | Local inference, privacy-safe computation |
| Jaeger AI | Local companion, macOS control, voice |
All workers are accessed through a common interface:
class ReasoningProvider(Protocol):
worker_id: str
provider: str
capabilities: list[str]
data_location: str # "local" | "cloud"
privacy_class: str # "local_only" | "external_provider" | "approved_provider"
async def generate(self, briefing: ContextBriefing, message: str) -> WorkerResult: ...
async def check_availability(self) -> AvailabilityStatus: ...
Filtered, budgeted context per task: identity, user context, project context, recent conversation, relevant memories, constraints, privacy policy, available tools, output requirements, and a manifest of what was included/excluded/redacted.
Structured result: content, artifacts, tool calls, confidence, cost report, metadata, and verification evidence.
jaeger_local)save_identity / select_character / make_default commands for assistant name/persona projection/api/companion normalized client surfacehermes_local)hermes --session <id> --prompt "<turn>" (or hermes run)~/.hermes/profiles/<name>/skills/) — ARES does not duplicate themdelegate_task (max_concurrent_children 3, max_spawn_depth 1 on this machine)claude_local)claude -p "<prompt>"; current ARES delegation is single-turn~/.claude/projects/**/*.jsonl (mode=ro, never write)--disallowedToolscodex_local)codex exec --skip-git-repo-check "<prompt>"; current ARES
delegation is single-turn~/.codex/sessions/** — detect, not parseollama_local)POST /api/chat with model name, streamingARES never imports a worker’s execution loop. Framework adapters expose both
streaming chat start and collected turn execution over their shared backend.
DispatchService uses the collected-turn seam for scheduled plan resumption;
primary browser chat continues to use streaming. Existing implementations do
not yet uniformly implement the aspirational ReasoningProvider shape above.
ARES may discover an external worker installation and invoke its supported
contract, but it does not import or copy the worker’s execution loop. Source
checkout discovery is a compatibility mechanism, not ownership: external stores
remain read-only and source mounts can be removed once an equivalent installed
client/contract is available. The detailed compatibility inventory remains in
rfcs/agent-source-boundary.md.
ARES WebUI supports an opt-in extension surface for self-hosted installs. Extensions can serve static assets and inject same-origin CSS/JS. They execute with full WebUI session authority — only enable extensions you wrote yourself or from sources you trust.
For contract-affecting changes, see docs/architecture.md for the full contract index, RFCs, and review expectations. Key contracts:
rfcs/canonical-session-resolution.mdrfcs/ares-run-adapter-contract.mdrfcs/agent-source-boundary.mdrfcs/turn-journal.md