Components
May 26, 2026 · View on GitHub
Priority Order
Pythia is the MVP — the thing that actually solves the Medusa problem. The renderer and waypoints are supporting infrastructure that feed it live environment data to score against. Build order:
- Pythia — tool/skill selection with scored recommendations
- Renderer — live context injection (feeds Pythia's env awareness)
- Checkpoints — lightweight session recovery (feeds Pythia's recency signal)
1. Pythia — Tool Oracle (perseus suggest) — MVP / Alpha
Given a task description and the current environment state, returns a ranked list of approaches: which Hermes skill to load, which integration to prefer, which path minimizes latency and maximizes fidelity.
This is the core value prop. Every other component exists to make this answer more accurate.
Interface
perseus suggest "download and summarize recent arxiv papers on RAG"
perseus suggest "download and summarize recent arxiv papers on RAG" --llm ollama
Output
Suggested approaches for: "download and summarize recent arxiv papers on RAG"
Environment: 2026-05-18 06:49 CT | Hermes WebUI ✅ | 87 skills loaded
1. ★★★ skill:arxiv + web_extract
Load the arxiv skill, search by keyword, fetch papers via web_extract.
Why: arxiv skill handles structured search + metadata; web_extract handles PDF→markdown.
Deps: all present. No service issues detected.
2. ★★☆ web_search + web_extract
Generic fallback. Works but loses structured arxiv metadata (categories, authors, IDs).
Use if: arxiv skill is unavailable or you need broader source coverage.
3. ★☆☆ skill:dspy RAG pipeline
Overkill for a one-off. Worth it if this becomes a recurring automated job.
Scoring Factors
| Factor | Signal Source | Weight |
|---|---|---|
| Skill availability | ~/.hermes/skills/ directory scan | High |
| Skill freshness | Last-modified date vs. 30-day threshold | Medium |
| Service health | @services render (live health checks) | High |
| Task complexity match | Heuristic: one-off vs. recurring, structured vs. ad-hoc | Medium |
| Recency signal | Last waypoint — what tools worked in recent sessions | Medium |
| Token/latency estimate | Rough cost model per approach type | Low |
Alpha / Phase 5A Scope
Perseus still supports the original structured prompt flow over live environment state. In addition, the current implementation supports an optional local-model path via perseus suggest --llm ollama[:model]. This keeps prompt generation as the core interface while allowing local inference when available.
Full autonomous scoring and learned ranking remain future milestones.
2. Renderer (perseus render)
Processes a source .md file with a @perseus header and produces plain markdown with all directives resolved. The assistant never sees directive syntax — only resolved values.
Source Format
Any standard .md file. Perseus activates when @perseus appears on the first line:
@perseus v0.4
@prompt
This document was rendered live. All values are current.
@end
# Session Context — @date format="YYYY-MM-DD HH:mm z"
...
No specialized file extension. Compatible with all markdown tooling, renderable as-is by GitHub, editors, and existing AI context systems. Existing AGENTS.md / CLAUDE.md files can opt in by adding @perseus to the first line.
Rendering Pipeline
source.md (@perseus header)
│
▼
[Parse] — tokenize directives vs. passthrough markdown
│
▼
[Resolve] — execute each directive (shell, file, http, env...)
│ ↕ cache layer (session / ttl / persist)
▼
[Conditional eval] — @if/@else blocks collapsed to one branch
│
▼
[Assemble] — stitch resolved values into final markdown
│
▼
rendered output → injected into assistant context
See directives.md for the full directive reference.
3. Checkpoints (perseus checkpoint)
Checkpoint state can now also be compared with perseus diff, which renders a field-level diff between two checkpoints or the latest pair.
Lightweight resumption snapshots. Written explicitly by the assistant at natural pause points — end of a task, before a large operation, at a logical handoff. Not automatic, not a log.
Design Principles
- Explicit over automatic. The assistant calls
perseus checkpointas a tool at the right moment. This keeps the implementation simple and the data meaningful — checkpoints are written when there's actually something worth resuming, not on a timer. - Lightweight. A checkpoint is a YAML file with 5-8 fields. It should take one tool call to write and one to read.
- Resumption-focused. Contains only what's needed to continue without re-orientation. Not a log, not an audit trail.
Write (assistant tool call)
perseus checkpoint \
--task "Rewriting webhook handler to validate Bearer token" \
--status "done — handler written and tested" \
--next "update .env.example with HERMES_WEBHOOK_SECRET placeholder" \
--workspace /workspace/hermes-ntfy \
--notes "JWT lib is python-jose; secret lives in .env as HERMES_WEBHOOK_SECRET"
All fields except --task are optional. Short and fast.
Recover
On session start, if a recent checkpoint exists, the renderer includes it under ## Last Session. The assistant sees task, status, next action, and notes — and can continue immediately.
Schema
version: 1
written: 2026-05-18T06:49:00-05:00
task: "Rewriting webhook handler to validate Bearer token"
status: "done — handler written and tested"
next: "update .env.example with HERMES_WEBHOOK_SECRET placeholder"
workspace: /workspace/hermes-ntfy
notes: "JWT lib is python-jose; secret lives in .env as HERMES_WEBHOOK_SECRET"
Configuration
Perseus reads ~/.perseus/config.yaml (global) with workspace-local .perseus/config.yaml taking precedence.
render:
cache_dir: ~/.perseus/cache
session_digest_count: 5
services_timeout_s: 3
shell: /bin/bash
checkpoints:
store: ~/.perseus/checkpoints
ttl_s: 86400 # stale after 24h; still kept, just not injected as live
max_keep: 30
pythia:
skill_dir: ~/.hermes/skills
stale_skill_days: 30
llm_provider: ollama
ollama_model: llama3.1
llm_timeout_s: 30
ollama_host: http://127.0.0.1:11434
assistant:
session_search_available: true
skills_list_available: true
4. Mnēmē (perseus memory) — Narrative Project Memory
Mnēmē distills checkpoints and Pythia log entries into a per-workspace narrative
markdown file stored at ~/.perseus/memory/<workspace-hash>.md. Snapshots tell you
where you are now; Mnēmē tells you how you got here.
Modes
- Deterministic (default): rule-based extraction. No LLM required. Sections: Project Arc, Key Decisions, Task History, Patterns & Anti-patterns, Recent Activity.
- LLM-assisted (opt-in): set
memory.llm_providertoollamaoropenai-compat. The narrative is distilled by a local model via the existingrun_llminfrastructure.
CLI
perseus memory update # incremental — only new checkpoints + Pythia entries
perseus memory compact # full re-distillation; increments compaction_count
perseus memory show # print the narrative file
perseus memory status # high-water marks, age, mode, size
perseus memory query "<question>" # grep (deterministic) or LLM Q&A
Auto-update
When memory.auto_update is true (default), perseus checkpoint calls the silent
Mnēmē update path after writing the checkpoint. A failure in Mnēmē prints a warning
and never aborts the checkpoint write.
Directive
@memory [focus="decisions|recent|patterns|arc"] [ttl=N] injects the narrative inline
(or a single named section) when a Perseus source file is rendered. See
spec/directives.md for the full reference.
5. Health (perseus health) — Context Maintenance Heuristics
Read-only deterministic maintenance report. Same content backs the @health
inline directive.
CLI
perseus health [--workspace <path>]
Heuristics
- Stale Checkpoints — older than
health.stale_checkpoint_days - Duplicate Checkpoints — repeated (task, status, next) in the last
health.duplicate_checkpoint_window - Context Source Size —
.perseus/context.mdline count exceedshealth.context_line_warning - Old Completed Tasks — Agora tasks closed more than
health.include_completed_tasks_older_than_daysdays ago
Config
health:
stale_checkpoint_days: 7
duplicate_checkpoint_window: 5
context_line_warning: 400
include_completed_tasks_older_than_days: 14
Read-only by design — never modifies files.
6. Agora (perseus agora) — Task Coordination Substrate
A flat-file task board. Each tasks/task-NN-*.md file owns YAML frontmatter
(id, title, status, scope, depends_on, claimed_by, opened, closed).
CLI
perseus agora list # group by status
perseus agora claim <task-id> --agent <name> # mark in_progress
perseus agora complete <task-id> # mark completed
Directive
@agora [status=...] [scope=...] renders the same view inline.
7. Daedalus (perseus oracle + --llm daedalus)
Two related surfaces:
Dataset curation
perseus oracle accept <log-id> # label as accepted
perseus oracle reject <log-id> # label as rejected
perseus Pythia log [--limit N] [--unlabeled]
perseus oracle export [--output FILE] [--format jsonl|alpaca]
perseus oracle infer-labels [--json]
perseus oracle outcomes [--dry-run] [--json]
perseus oracle drift [--json]
log-idacceptslatest, full timestamp, or timestamp prefixexportwrites ONLY entries withaccepted=true- Atomic rewrite (
.tmp+os.replace); original log never partially mutated outcomesannotates accepted/inferred-accepted entries with deterministic checkpoint-correlated reinforcement signals:completed/completion_signalcheckpoint_counterror_count/error_ratetime_to_completion_s
suggestconverts recent outcome objects into transparent prompt hints: successful completed outcomes boost related recommendation tokens; incomplete or error-heavy outcomes lower them. No-data behavior is neutral.- Opt-in A/B exploration (
pythia.ab_testing_enabled) selects deterministic primary/alternate candidates from outcome-weight hints, labels the prompt with an exploration id, and storesab_testmetadata in the Pythia log.
Local model routing
perseus suggest "task" --llm daedalus
Routes through Ollama using llm.daedalus_model (default perseus-daedalus) at
llm.daedalus_url (default http://localhost:11434). The fine-tuned model is a
user concern; Perseus only handles data export and request routing.
8. Inbox (perseus inbox) — Point-to-Point Messages
Per-workspace message store, parallel to checkpoints.
Storage: ~/.perseus/inbox/<workspace-hash>/<timestamp>-<sender>.yaml
CLI
perseus inbox send "subject" --body "..." [--recipient X] [--from Y] [--workspace .]
perseus inbox list [--unread] [--all]
perseus inbox read <id-prefix|latest>
perseus inbox dismiss <id-prefix>
Directive
@inbox [unread=true] [limit=N] — renders pending messages inline. Dismissed
messages are always excluded. unread=true filters to unread.
Config
inbox:
store: ~/.perseus/inbox
default_recipient: anyone
default_sender: perseus
9. Init, Templates, and Profiles (perseus init)
Curated starter .perseus/context.md files keyed by AI assistant.
Shipped templates: generic, hermes, rovodev, claude-code, cursor.
Phase 16 adds product profiles. A profile creates a portable context source and
.perseus/pack.yaml manifest instead of only a loose template.
CLI
perseus init --template hermes
perseus init --list-templates
perseus init --profile generic
perseus init --list-profiles
Discovery
$PERSEUS_TEMPLATE_DIRif set<dir-of-perseus.py>/templates/- Embedded
INIT_CONTEXT_TEMPLATE(legacy default — used when no--template)
9.1 Context Packs (perseus pack)
Context packs are optional manifests at .perseus/pack.yaml. They name the
workspace profile, trust profile, render targets, assistant output files, and
optional cited-synthesis source packs.
CLI
perseus pack validate
perseus pack validate --json
perseus pack show --workspace /path/to/workspace
perseus pack validate returns 0 for valid packs and 1 for invalid or
missing manifests. Existing .perseus/context.md workflows do not require a
pack.
Manifest Shape
version: 1
name: generic-context
profile: generic
trust_profile: balanced
renders:
- name: default
source: .perseus/context.md
output: live-context.md
assistant: generic
synthesis:
- name: project-status
question: What is the current project status and next allowable action?
sources: [ROADMAP.md, HANDOFF.md, README.md]
enabled: false
Supported profiles in Phase 16: generic, hermes, codex,
claude-code, cursor, and rovodev.
10. Serve (perseus serve) — Read-Only HTTP View
Stdlib HTTP server for browsing rendered workspace state.
CLI
perseus serve [--port 7991] [--host 127.0.0.1] [--workspace .]
perseus serve --generate-token
Endpoints
| Path | Returns |
|---|---|
/ | HTML index linking to other endpoints |
/context | text/markdown — perseus render .perseus/context.md output |
/narrative | text/markdown — Mnēmē narrative body |
/health | text/markdown — health report |
/agora | text/markdown — Agora list |
/checkpoint/latest | text/yaml — workspace pointer or global latest |
/oracle/log?limit=N | application/json — recent Pythia log entries |
POST returns 405. By default, serve binds to localhost and requires no auth for
backward compatibility. If serve.auth_token is set, every HTTP endpoint
requires Authorization: Bearer <token> and unauthorized requests return:
{"error": "unauthorized"}
Non-loopback binds require either serve.auth_token or an explicit insecure
opt-in via serve.allow_insecure_remote: true / --i-understand-no-auth.
11. Watch (perseus watch) — Portable Headless Refresh
perseus watch runs a foreground polling loop that refreshes rendered context
outputs without cron, launchd, systemd, or filesystem watcher dependencies.
CLI
perseus watch
perseus watch --source .perseus/context.md --output .hermes.md
perseus watch --workspace /path/to/workspace --interval 2
perseus watch --manifest .perseus/pack.yaml
Behavior
- Defaults to
.perseus/context.md->.hermes.mdin the current workspace. - If
.perseus/pack.yamlexists and no single source/output override is supplied, everyrenders:entry becomes a watch target. - Polling defaults to
watch.poll_interval_s: 5. - Detected changes are debounced by one additional poll interval before render.
- Render failures are logged and the loop continues unless
--exit-on-erroris set. - Watched sources and outputs must remain inside the workspace unless
--allow-outside-workspaceorrender.allow_outside_workspaceis set. - SIGINT and SIGTERM request a clean shutdown and emit a final
[watch] stoppedlog line.
Watch mode is the platform-agnostic foreground alternative to scheduler-owned cron/launchd/systemd refresh.
12. LSP (perseus serve --lsp) — Editor Integration
The same serve command can run a Language Server Protocol server over stdio
or a loopback TCP port. The LSP surface is read-only by default and derives
directive names, completions, and hover safety from DIRECTIVE_REGISTRY.
CLI
perseus serve --lsp --stdio
perseus serve --lsp --tcp 7992
perseus serve --lsp --stdio --allow-lsp-mutations
Supported LSP Features
initialize,shutdown, andexit- full-document sync via
didOpen/didChange/didClose - diagnostics for unknown directives, malformed cache TTLs, unclosed blocks, and unsubscribed federation aliases
- completion for directive names and registered directive arguments
- hover previews only for directives marked safe for hover
workspace/executeCommandfor render/openCheckpoint, with mutation commands gated behind--allow-lsp-mutations
12. Doctor (perseus doctor) — Readiness Probe
perseus doctor reports whether the workspace and global state are ready for a
healthy render/session. It is intentionally read-only and supports JSON output
for CI or agent callers.
CLI
perseus doctor [--workspace <path>] [--json]
Checks
- config parseability
- workspace context file presence
- render trust gates
- latest checkpoint age
- Mnēmē narrative health
- federation manifest/subscription health
- Pythia log readability
- serve loopback default
- directive registry invariants
13. Schema Validation (schema=, @validate, perseus validate)
Phase 12 adds a pure-Python schema validation engine. pyyaml remains the only
required dependency; the schema subset is documented in spec/data-model.md.
Render-Time Validation
@query ... schema="name"validates YAML stdout before injecting it.@read ... schema="name"validates a full file, extractedpath=,.envkey=, or fallback.@env ... schema="name"validates environment values and fallbacks.@validate schema="name" ... @endrenders a block, parses the payload, and emits a visible warning instead of invalid context.DirectiveSpec.output_schemacan declare directive-wide rendered-output invariants. Per-invocationschema=takes precedence.
CLI
perseus validate --schema service payload.yaml
perseus validate --schema service --json payload.yaml
cat payload.yaml | perseus validate --schema service -
Exit codes are 0 for valid payloads, 1 for validation failures, and 2 for
schema/input read or parse errors.
14. Directive Graph (perseus graph) — Static Dependency Substrate
perseus graph scans a source document without rendering it and emits the
directives it contains. It is the Phase 13 read-only substrate for predictive
pre-fetching.
CLI
perseus graph .perseus/context.md
perseus graph .perseus/context.md --json
The graph skips fenced code blocks and derives directive metadata from
DIRECTIVE_REGISTRY: directive kind, safety flags, cacheability, and summary.
It also includes static resource hints for file/path/env-style directives such
as @read, @include, @list, @tree, and @env.
The graph uses the same directive alias expansion as the render pipeline before
extracting nodes, so shorthand such as @q appears as its canonical directive
in graph and prefetch output.
The command never executes shell-backed directives.
15. Pattern Prefetch (perseus prefetch) — Rule-Based Cache Warming
perseus prefetch applies explicit prefetch.rules to the static directive
graph and warms the existing directive cache before a render. It does not
render the source document first.
CLI
perseus prefetch .perseus/context.md
perseus prefetch .perseus/context.md --json
Config
prefetch:
rules:
- name: status-diff
trigger: '@query "git status"'
prefetch:
- '@query "git diff --stat" @cache ttl=300'
adaptive:
enabled: true
backend: deterministic # or daedalus
threshold: 0.5
max_candidates: 5
candidates:
- id: decision-memory
prefetch: '@memory focus=decisions @cache ttl=300'
patterns: ["decision", "memory"]
Rules can use a string trigger such as @query "git status" or a mapping with
directive, kind, args, args_pattern, args_contains,
resource_kind, and resource. Prefetch directives must be inline,
cacheable directives and must include @cache ttl=N, @cache persist, or
@cache session.
Adaptive prefetch is disabled by default. When enabled, it scores only
predeclared adaptive.candidates. The deterministic backend reads recent
accepted Pythia entries and the workspace Mnēmē narrative for pattern matches.
The daedalus backend routes through existing LLM plumbing and falls back to
deterministic scoring on transport, provider, or parse errors. Daedalus may
rank candidates; it must not generate new directives or context prose.
The command reports every ran, skipped, or failed prefetch. It respects existing
render trust gates such as render.allow_query_shell; cache writes are the only
intended side effect.
16. Cited Synthesis (perseus synthesize) — Bounded Curator Surface
Phase 15A adds an explicit command surface for cited synthesis. It does not
change perseus render and does not make generated text part of ordinary
resolved context.
perseus synthesize "What is the next allowable action?" \
--source ROADMAP.md \
--source HANDOFF.md
Without --llm, the command prints the prompt that would be sent to a model.
With --llm, generation still requires generation.enabled: true or
--enable-generation.
The LLM is a drafter, not an authority. Accepted claims must include:
- claim text
- at least one source id
- source path/label
- line range
- exact quote present in that line range
Claims without validated citations are dropped. JSON output keeps accepted
claims, dropped_claims, sources, model metadata, and guardrail metadata
separate so downstream agents can inspect what survived validation.
Redaction applies recursively to every string in the synthesis result before CLI output, including nested claim citations, dropped claims, source quotes, and raw prompt/model-response fields.
17. Cron (perseus cron) — POSIX Scheduling
Generates a crontab entry for periodic rendering. The print path is host-neutral;
--install works where crontab is available, including macOS, Linux, BSD, and
WSL.
Recommended over perseus launchd / perseus systemd when portability matters.
Native Windows Task Scheduler integration is deferred; use WSL cron, the printed
render command, or invoke perseus render from a Windows scheduler manually.
CLI
perseus cron .perseus/context.md -o AGENTS.md --every 5
perseus cron .perseus/context.md -o AGENTS.md --every 5 --install
--every accepts minutes; 1, 60, and >60 are translated to the
appropriate * * * * * / 0 * * * * / 0 */N * * * schedule.
--install appends the entry to the user's crontab via
crontab -l → edit → crontab -. Entries are tagged # perseus-render for
easy lookup.
18. Mnēmē Federation (task-19, Phase 8.2)
Cross-workspace narrative aggregation. Lets one workspace subscribe to another workspace's Mnēmē narrative so curated project memory flows across related projects on the same filesystem.
Storage: ~/.perseus/memory/federation.yaml (path configurable via
memory.federation_manifest). Schema:
version: 1
subscriptions:
- alias: support
path: /workspace/support-agent
enabled: true
- alias: hermes
path: /workspace/hermes
enabled: true
notes: primary mentor agent # reserved field — preserved on round-trip
The list-of-objects shape is intentional (per Q1) so v2 fields like
share:, stale_after:, include_sections: can be added without
migrating existing manifests.
CLI:
| Command | Effect |
|---|---|
perseus memory federation list | Table of aliases + status (ok / stale / ⚠ unavailable) + paths |
perseus memory federation subscribe <alias> <path> | Add a subscription. Validates alias against [a-zA-Z0-9_-]+; warns (does not refuse) on missing path or duplicate resolved path |
perseus memory federation unsubscribe <alias> | Remove a subscription. Exits 1 if alias not found |
perseus memory federation pull | Re-read all narratives — diagnostic only, never mutates the manifest |
Directive: see spec/directives.md § @memory federation.
Scope (per Q2): federation reads ~/.perseus/memory/<hash>.md only.
Checkpoints, Pythia logs, inboxes, task files, health reports, and
rendered full context are out of scope for v1.
Synchronisation (Q4): the directive re-reads narratives on every render.
There is no cache and no daemon. The CLI is side-effect-free except for
subscribe and unsubscribe, which mutate the manifest atomically (tmp file
os.replace).
Failure modes (Q5): missing/unreadable narratives produce inline warning blocks; render never silently skips and never hard-fails.
Privacy (Q6): subscriber-side only. No publisher-side ACLs. Any filesystem-trust assumption the user already makes between two workspaces is the trust assumption federation inherits.
19. MCP Server
perseus mcp serve exposes registered directives as Model Context Protocol
tools. Tool visibility and direct invocation share the same policy checks:
mcp.tool_blocklist denies listed tools, mcp.tool_allowlist narrows the
callable surface when present, and sensitive shell-backed tools such as
perseus_query and perseus_agent require explicit allowlist opt-in.
The policy is enforced at both tools/list and tools/call so a client cannot
invoke a hidden or blocked tool by name.