Files
2026-06-28 14:21:29 +00:00

4.6 KiB

Hermes Spec

Status: active / matches code.

"Hermes" is PodMan's action layer — the part that turns a detected problem into something a teammate sees, hears, or gets done. It spans three things:

  1. Interventions — cards, messages, and urgent voice in the pod room.
  2. Async jobs — longer tasks delegated from the live conversation agent.
  3. Ops watchdog — keeps the production services healthy.

The LiveKit identity for the main agent is podman-hermes.


1. Interventions

Code: backend/src/agent/podman.ts, backend/src/action/hermes.ts, backend/src/voice/live.ts.

When the agent detects a collision, it runs the learning loop (recall → policy gate; see docs/cont_learning.md) and then publishes the least intrusive intervention that fits:

  • Card / message — a data-channel packet on the podman.intervention topic (publishHermesIntervention / publishHermesMessage). Default path.
  • Urgent voice — only for critical collisions. speak() generates Gemini TTS audio and publishes it as a LiveKit audio track.
  • Research overlap nudge — a collaboration card when one engineer is editing a file while another is researching the same topic in docs/browser context. This uses suggestedAction.kind = "ping_teammate" and is spoken once for the demo beat, but it is explicitly not a merge conflict.

Intervention text is short and deterministic (template, not an LLM call): Conflict: alice + bob both on detector.ts (unpushed). Seen before. The spoken line is phrased for natural TTS prosody. Each intervention is persisted to the interventions collection; the teammate's accept/dismiss returns via POST /api/outcome.

Research-overlap text is also deterministic: 🤝 bob is researching LiveKit agents (docs.livekit.io) while alice edits livekit.py — sync up before duplicating effort.

A per-pod cooldown (NUDGE_COOLDOWN_MS, default 3 min) and a single-shot "active conflict" guard prevent repeat nagging; a conflict re-arms once it resolves.


2. Async Hermes jobs

Code: backend/src/hermes/jobs.ts. Storage: hermes_jobs + hermes_job_events (see docs/mongodb.md).

The live conversation agent can hand a longer task to Hermes via its delegate_to_hermes tool. Lifecycle:

queued → running → (waiting_for_confirmation) → completed | aborted | failed

createHermesJob() records the job, emits an accepted event, and kicks off runHermesJob() in the background. The runner gathers context and runs scoped, read-mostly steps based on the prompt and success criteria:

  • always: git status --short --branch, git diff --stat
  • if the ask mentions GitHub: a repo reachability check via the GitHub API
  • if it mentions Mongo/memory/telemetry: collection counts
  • if it mentions build/test/typecheck/broken: pnpm typecheck

Confirmation gate: if riskLevel === 'deploy_allowed' and requiresConfirmation, the job parks at waiting_for_confirmation instead of acting. Abort: abortHermesJob() signals the runner's AbortController.

Every step appends a hermes_job_event (redacted + truncated), which is both stored and published live to the room as a HERMES_JOB_EVENT data message from a short-lived podman-hermes-job-* identity. The conversation UI streams these via GET /api/.../hermes-job/events/stream.

Endpoints: POST /api/internal/hermes/jobs, GET /api/internal/hermes/jobs/:jobId, .../abort, .../events, .../events/stream, plus the pod-scoped .../live-conversation/:sessionId/hermes-job.


3. Ops watchdog

Code: scripts/hermes-watchdog.mjs, scripts/hermes-sync-deploy.mjs, scripts/hermes-notify.mjs. Detail: docs/digitalocean.md.

systemd supervises the app processes; Hermes owns the loop around them:

  • pnpm hermes:watchdog checks systemd services, public routes, /health, /api/pods, and pnpm deploy:doctor. Failures trigger targeted restarts.
  • podman-hermes-watchdog.timer runs it every 5 minutes.
  • podman-hermes-sync-deploy.timer polls origin/main every 2 minutes and, on a clean tree, fast-forwards, builds, publishes frontend/dist, restarts API/agent/Caddy, and runs the strict watchdog.
  • Reports go to /var/log/podman/hermes-watchdog-latest.json; set PODMAN_ALERT_WEBHOOK_URL to forward failures to Discord/Slack/webhook.

What Hermes is NOT

  • Not an autonomous code-writing agent. Job steps are scoped, read-mostly checks; deploy-level actions require explicit confirmation.
  • Not a second collision detector. Detection is deterministic (collision/detector.ts); Hermes only acts on the result.