Skip to main content
Glama

vanth

Ereignisgesteuerte Hintergrundjobs für Agenten.

Vanth ist ein lokaler Hintergrundjob-Daemon mit einer Model Context Protocol (MCP)-Schnittstelle. Er führt abgekoppelte, nicht-interaktive Shell-Befehle aus, erfasst deren Ausgaben dauerhaft, parst optionale strukturierte AGENT_EVENT-Ereignisse in Fortschrittsbalken, Metrikreihen und Prüfpunkte und kann eine Codex- oder OpenCode-Sitzung wecken, wenn ein Job Aufmerksamkeit benötigt. Er ist für einen vertrauenswürdigen Benutzer auf einem Rechner konzipiert.

  • Beliebige Befehle: Downloads, Bild-/Audioverarbeitung, ETL, ML-Training – wenn es in einer Shell läuft, kann Vanth es abgekoppelt ausführen und verfolgen.

  • Dauerhaft: Jobs und Ereignisse leben in SQLite (WAL, Busy-Timeout) und überstehen Daemon-, MCP- und Maschinenneustarts.

  • Ereignisorientiert: Agenten job_wait auf sinnvolle Ereignisse, anstatt Protokolle abzufragen.

  • Aufmerksamkeitsweckung: Dauerhafte Zustellungen mit mindestens einmaliger Auslieferung setzen einen Codex-Thread oder eine OpenCode-Sitzung fort, wenn ein Job einen Menschen oder Agenten benötigt.

  • Terminal-Dashboard: Der native Go-Monitor monitor rendert ein Live-Dashboard im W&B-LEET-Stil mit Jobs, Metriken und Diagrammen.

Nicht im Fokus für v1: Fernzugriff, TLS, Multi-User-Mandantenfähigkeit/RBAC, Kontingente, interaktive Standardeingabe und eine Weboberfläche.

Für Agenten: Starten Sie Arbeit mit job_start, dann job_wait für progress/checkpoint/completed-Ereignisse anstatt zu pollend; lassen Sie Jobs AGENT_EVENT-Zeilen (siehe unten) ausgeben, damit Fortschritt, Metriken und Prüfpunkte live im vanth-monitor-Dashboard erscheinen; und lassen Sie lange Jobs Sie über Weckziele fortsetzen, anstatt dass Sie selbst nach dem Rechten sehen.


Schnellstart

Installation (erfordert uv; läuft auf Python 3.11+):

uv tool install vanth

Dies installiert den MCP-Server vanth, den Daemon vanthd, das vanth-monitor und die Ops-CLI als eigenständige Werkzeuge (das Wheel bündelt den nativen Go-Monitor, daher wird keine Go-Toolchain benötigt).

Aus einem Quellcode-Checkout (Entwicklung):

git clone https://github.com/abhim-dv/vanth.git && cd vanth
uv sync

Starten Sie den Daemon (halten Sie dieses Terminal offen):

uv run vanthd

Starten Sie in einem zweiten Terminal einen verfolgten Job über den MCP-Server:

uv run vanth

oder verwenden Sie die Werkzeuge direkt von einem MCP-Client aus (siehe MCP-Integration).

Überprüfen Sie, ob alles gesund ist:

job_doctor()

Durchgängig: einen verfolgten Job ausführen

Sobald der MCP-Client verbunden ist, ist dies die gesamte Schleife:

job_start(
  command="uv run python examples\\long_job.py",
  name="demo run",
  notify_on=["checkpoint", "failed", "completed"],
)
# -> job_<id>

job_wait(job_id="job_<id>", filters=["checkpoint"], timeout_seconds=120)
# -> returns the first checkpoint event + current status

job_wait(job_id="job_<id>", filters=["completed", "failed"], timeout_seconds=300)
# -> returns the terminal event + exit code

Und in einem dritten Terminal sehen Sie live zu:

uv run vanth-monitor

Befehlszeilen-Einstiegspunkte

Befehl

Zweck

uv run vanth

MCP-Stdio-Server (Brücke zum Daemon); auch status / doctor / restart-Unterbefehle

uv run vanthd

Der Hintergrund-HTTP-Daemon

uv run vanth-monitor

Live-Terminal-Dashboard (Go-Binärdatei, im Wheel gebündelt)

uv run vanth-codex-notify

Zustellungsadapter: liest eine Wecknutzlast von stdin, leitet sie an Codex weiter

Operations-CLI

uv run vanth status              # is the daemon up? pid, schema, running jobs, deliveries
uv run vanth status --json       # machine-readable version
uv run vanth doctor              # full health report (same as job_doctor, human-readable)
uv run vanth restart             # gracefully stop + start the daemon (jobs survive)
uv run vanth setup               # register the MCP server in your clients' configs
uv run vanth setup --remove      # unregister it

vanth restart ist die zuverlässige Methode, um ein Code-/Versionsupdate zu übernehmen: Es sendet dem Daemon eine sanfte Herunterfahr-Anfrage über Loopback, wartet, bis der alte Prozess die Heimsperre vollständig freigegeben hat, und startet dann einen neuen Daemon. Laufende Jobs gehören zu abgekoppelten Ausführenden, daher laufen sie über den Neustart hinweg weiter.


Related MCP server: Background Process MCP

Funktionsweise

MCP client / HTTP client
        |
        v
   vanthd (localhost HTTP daemon, bearer-token auth)
        |                 |                    |
        |                 |                    +---> wake adapters
        |                 |                          (local_command / codex_thread / opencode_thread)
        |                 |
        |                 +----> jobs.sqlite (durable source of truth)
        |
        +----> vanth.runner (detached worker process)
                    |
                    +----> your command (own process group)
                              |
                              +----> stdout/stderr -> logs/ + AGENT_EVENT parsing

Eigentumsregeln:

  • der Ausführende besitzt den eigentlichen Befehl, sein Timeout und das Stream-Draining;

  • der Daemon besitzt Wartung, Zustellungsversand, API-Anfragen und Wiederherstellung;

  • SQLite ist die Quelle der Wahrheit über Prozessneustarts hinweg;

  • die MCP- und HTTP-Clients müssen niemals am Leben bleiben, damit Jobs fortgesetzt werden können.

Ein Job gilt erst dann als abgeschlossen, wenn beide Ausgabeströme das EOF erreicht haben und alle strukturierten Ereignisse persistiert wurden.

Job-Lebenszyklus

Ein Job durchläuft eine kleine Menge von Zuständen. Endzustände sind dauerhaft.

Zustand

Bedeutung

running

Arbeitslast gestartet; Ausführender streamt Ausgabe und sendet Heartbeats

completed

Befehl mit Exit-Code 0 beendet, Ströme geleert, Ereignisse persistiert

failed

Befehl mit Exit-Code ungleich 0 beendet

timeout

Befehl hat timeout_seconds überschritten; Ausführender hat ihn beendet

cancelled

job_stop wurde ausgeführt und der Prozessbaum tatsächlich beendet

orphaned

Ausführender unerwartet gestorben (Absturz); niemals stillschweigend fallen gelassen

Der Ausführende setzt timeout_seconds auch über Daemon-Neustarts hinweg durch. Bei der Wiederherstellung wird ein running-Job, dessen Ausführender verschwunden ist, als cancelled (wenn ein Stopp angefordert wurde) oder orphaned (wenn nicht) markiert – niemals als Zombie-running-Zeile belassen.


Installation des MCP-Servers

vanth ist der MCP-Stdio-Server. Er kommuniziert mit dem Daemon und startet ihn bei erster Verwendung automatisch, falls er noch nicht läuft.

Einmalige Einrichtung

Nach der Installation des Werkzeugs verbinden Sie es in einem einzigen Schritt mit den MCP-Clients auf Ihrem Rechner:

uv tool install vanth
vanth setup

vanth setup erkennt Ihre installierten Clients (opencode, Codex und generische mcpServers-Clients wie Claude Code / Cursor), zeigt an, was gefunden wurde, sichert jede Konfiguration vor dem Bearbeiten (.vanth-setup-<ts>.bak) und fügt den Vanth-MCP-Eintrag ein oder aktualisiert ihn – dabei bleiben alle anderen Einstellungen und Kommentare unberührt.

vanth setup                  # detect + configure everything found (prompts)
vanth setup --yes            # apply without prompting (scripts/CI)
vanth setup opencode codex   # only specific clients
vanth setup --json           # machine-readable result
vanth setup --remove         # remove the Vanth MCP entries instead

Konfigurationen, die verwaltet werden:

Client

Datei

Abschnitt

opencode

~/.config/opencode/opencode.json

mcp.vanth

Codex

~/.codex/config.toml

[mcp_servers.vanth]

Claude Code / Cursor

~/.claude.json

mcpServers.vanth

Manuell sehen die gleichen Einträge wie folgt aus:

opencode

Fügen Sie zu ~/.config/opencode/opencode.json hinzu:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "vanth": {
      "type": "local",
      "command": ["vanth"],
      "enabled": true,
      "timeout": 15000
    }
  }
}

Verwenden Sie bei einem Quellcode-Checkout uv direkt anstelle eines nackten vanth:

{
  "mcp": {
    "vanth": {
      "type": "local",
      "command": ["uv", "run", "--directory", "/path/to/vanth", "vanth"],
      "enabled": true,
      "timeout": 15000
    }
  }
}

Überprüfen Sie die Verbindung und die Werkzeuge:

opencode mcp list

Claude-artige MCP-Clients (mcpServers)

Veröffentlichtes Wheel:

{
  "mcpServers": {
    "vanth": { "command": "vanth", "env": { "VANTH_HOME": "C:/Users/you/.vanth" } }
  }
}

Bei einem Quellcode-Checkout:

{
  "mcpServers": {
    "vanth": {
      "command": "uv",
      "args": ["--directory", "/path/to/vanth", "run", "vanth"],
      "env": { "VANTH_HOME": "C:/Users/you/.vanth" }
    }
  }
}

Konfiguration des Daemon-Heimverzeichnisses

Sowohl der MCP-Server als auch der Daemon lösen das gleiche Zustandsstammverzeichnis aus VANTH_HOME auf (Standard %USERPROFILE%\.vanth unter Windows, ~/.vanth unter Unix; AGENT_BG_HOME wird als Alias akzeptiert). Wenn beide gesetzt sind, müssen sie auf dasselbe Verzeichnis verweisen.


Instrumentierung von Jobs mit agent_event

Jedes Python-Skript kann strukturierte Ereignisse an stdout (oder stderr) ausgeben, die Vanth parst und der Monitor grafisch darstellt. Dies ist optional – einfache Skripte laufen und protokollieren weiterhin –, aber es macht aus einem Job ein erstklassiges verfolgtes Objekt.

from vanth.agent_events import agent_event, progress

# A checkpoint: something meaningful happened.
agent_event("checkpoint", "epoch complete", epoch=10, val_loss=0.42)

# A progress update: drives the progress bar and progress.* plots.
progress(10, 100, unit="epoch", stage="train", message="10/100 epochs")

# Arbitrary scalar metrics: become their own line plots.
agent_event("metric", _step=10, loss=0.42, acc=0.88, mbps=12.4)

Hinweise:

  • Der Helfer gibt AGENT_EVENT {json} mit flush=True aus (Flush ist wichtig);

  • progress(current, total, unit=..., stage=...) berechnet percent für Sie;

  • metric-Nutzlasten: numerische Felder werden zu Reihen; _step (falls vorhanden und numerisch) ist die x-Achse, andernfalls wird die Ereignis-Sequenznummer verwendet; Schlüssel, die mit _ beginnen (außer _step), werden ignoriert; boolesche Werte sind keine Metriken; NaN/Infinity/Null-Werte werden übersprungen und im Warnungsabzeichen des Monitors gezählt;

  • jedes andere Feld (z. B. file, stage, phase) wird beibehalten und in der Ereignistabelle sichtbar.

Beispiel: ein verfolgter Downloader

# downloader.py
import os
from vanth.agent_events import agent_event, progress

files = ["a.bin", "b.bin", "c.bin"]
total = sum(os.path.getsize(f) for f in files)
done = 0

for f in files:
    agent_event("checkpoint", f"starting {f}", file=f)
    # ... download f ...
    done += os.path.getsize(f)
    progress(done, total, unit="bytes", stage="download",
             message=f"{done}/{total} bytes")

Beispiel: eine Bildverarbeitungs-Batch

from vanth.agent_events import agent_event, progress

images = list(find_images("input/"))
for i, img in enumerate(images, 1):
    out = process(img)                    # resize, denoise, ...
    agent_event("metric", _step=i, sharpness=out.sharpness, size_mb=out.size_mb)
    progress(i, len(images), unit="images", stage="process", message=img.name)

Zeitgestempeltes, abgestuftes Logging mit loguru

Vanth liefert einen loguru-Wrapper mit, der jeden Eintrag in eine strukturierte AGENT_EVENT-Logzeile umleitet, sodass Protokolle als zeitgestempelte, stufenbewusste Ereignisse in der Ereignistabelle erscheinen (mit dem Stufenabzeichen und genauen Zeitstempeln) anstelle von nacktem Text:

from vanth.agent_logger import logger, log_with_context

logger.info("training started", lr=8e-5, batch_size=8)     # event type "log", level info
logger.warning("low disk", free_gb=2.5)
log_with_context("error", "failed to load checkpoint", path="best.pt")

Jeder Aufruf gibt AGENT_EVENT {"type":"log","level":"info","message":"...","data":{...}} aus, was der Daemon als dauerhaftes Ereignis persistiert. data trägt zusätzlichen Kontext. Der Monitor zeigt diese in der Ereignistabelle zusammen mit metric/progress-Ereignissen an.


Werkzeugreferenz (alle 20 MCP-Werkzeuge)

Werkzeug

Zweck

job_start

Startet einen Befehl als abgekoppelten Job

job_rerun

Startet einen Job mit seinem ursprünglichen Befehl/Umgebung/Arbeitsverzeichnis/Zielen neu

job_wait

Blockiert, bis ein passendes Ereignis (oder Timeout) eintritt – die bevorzugte Methode, um auf Jobs zu warten

job_status

Status, Befehl, Umgebung, Fortschritt, letztes Ereignis, Verknüpfungen, Tags eines Jobs

job_list

Letzte Jobs, filterbar nach status / thread_id / name / tags

job_view

Agentenorientierte Zusammenfassungen, sortiert nach Aufmerksamkeitspriorität

job_events

Strukturierte Ereignisse eines Jobs (vorwärts über since_event_id oder neueste zuerst über reverse)

job_tail

Begrenzter stdout/stderr-Log-Auszug mit Byte-Offsets

job_metrics_query

Liest gespeicherte skalare Metrikreihen (Verlust, Genauigkeit, Fortschritt.Prozent, ...)

job_metric_compare

Vergleicht eine Metrik über mehrere Jobs hinweg (neuester/Mittelwert/Min/Max/Summe/Anzahl)

job_run_summary

Ein Aufruf "Hat es funktioniert?" – Status, Laufzeit, Fortschritt, Metriken, Artefakte

job_artifact_add

Fügt einem Job ein Artefakt (Prüfpunkt, CSV, Ausgabe) hinzu

job_artifacts

Listet Artefakte auf, die einem Job zugeordnet sind

job_dashboard

Heruntersampelte Diagrammdatenansicht für beliebige Renderer

job_deliveries

Weckzustellungen für einen Job, filterbar nach status

job_mark_delivery

Setzt manuell den Status einer Zustellung

job_retry_delivery

Stellt eine fehlgeschlagene Zustellung erneut in die Warteschlange

job_delivery_attempts

Versuchs-/Leihverlauf einer Zustellung

job_stop

Stoppt einen laufenden Job (beendet den Prozessbaum)

job_doctor

Daemon-Gesundheit, Schema, Tabellen, Binärverfügbarkeit

job_cleanup

Trockenlauf oder echte Entfernung alter abgeschlossener Jobs

job_start

job_start(
  command="uv run python examples\\long_job.py",
  name="training run",
  cwd="F:\\git\\project",            # optional
  env={"CUDA_VISIBLE_DEVICES": "0"}, # optional
  timeout_seconds=3600,              # optional; None = no timeout
  notify_on=["progress","checkpoint","failed","completed"],
  origin_thread_id="019f...",        # the agent thread that launched it
  tags=["training","gpu"],           # optional
  wake_targets=[...]                 # optional, see below
)

Gibt job_id, status, worker_pid und die Log-/Ereignispfade zurück.

job_status – sehen, was ein Job tut

job_status(job_id="job_...")

Gibt Status, Befehl, Arbeitsverzeichnis, Umgebung, timeout_seconds, Notizen, Lauf (Autor, Hostname, Betriebssystem, Python-Version, CPU/GPU, Git-Repo/Branch/Commit), runtime_seconds, Fortschritt, letztes Ereignis, Thread-Verknüpfung, Tags und Exit-Code zurück. Dies ist der schnellste Weg für einen Agenten, um zu beantworten: "Was macht dieser Job?" – und spiegelt die Laufübersicht wider, die Sie für einen Lauf in W&B sehen würden.

Übergeben Sie notes="..." an job_start, um einen Lauf zu annotieren ("Was macht diesen Lauf besonders?"), was bei job_rerun erhalten bleibt und im Monitor angezeigt wird.

job_rerun – einen fehlgeschlagenen Job neu starten

job_rerun(job_id="job_...")

Startet den Job mit seinem ursprünglichen Befehl, Arbeitsverzeichnis, Umgebung, Timeout, Namen, Tags, Ursprungs-Thread und Weckzielen neu – eine neue job_id wird zurückgegeben. Verwenden Sie es, um einen fehlgeschlagenen Download, eine fehlerhafte Verarbeitungs-Batch oder einen vorübergehenden Fehler zu wiederholen, ohne die Anfrage neu zu konstruieren.

job_list – nach Name oder Tag filtern

job_list(status=["running"], name="train", tags=["gpu"], limit=20)

Filter: status (Liste), thread_id, name (Teilzeichenkette), tags (muss alle aufgelisteten Tags enthalten).

job_events – vorwärts oder neueste zuerst

job_events(job_id="job_...", since_event_id="evt_...", limit=20)      # events after the cursor
job_events(job_id="job_...", reverse=true, limit=20)                   # the 20 newest events, newest first

reverse: true gibt die aktuellsten Ereignisse zurück (neueste zuerst) – ideal für "Was ist kürzlich passiert?" – und kann mit since_event_id kombiniert werden, um rückwärts zu blättern.

job_wait — das Herz der Agentennutzung

job_wait(job_id="job_...", filters=["checkpoint","failed","completed"], timeout_seconds=3600)
  • wartet auf das erste Ereignis, das einem Filter entspricht, und gibt es mit dem aktuellen Status zurück;

  • übergebe since_event_id, um nur auf Ereignisse zu warten, die neuer sind als eines, das du bereits gesehen hast;

  • bei Zeitüberschreitung wird result: "timeout" zurückgegeben; bei Daemon-Herunterfahren wird result: "shutdown" zurückgegeben.

job_view — was dem Benutzer angezeigt werden soll

job_view(thread_id="019f...", limit=20)

Gibt kompakte Zusammenfassungen zurück, sortiert nach Aufmerksamkeitspriorität: laufende und fehlgeschlagene Jobs zuerst, dann Jobs mit ausstehenden/fehlgeschlagenen Zustellungen, dann alles andere. Jeder Eintrag enthält Status, Fortschritt, das letzte Ereignis, Thread-Verknüpfung, Tags und Zustellungsanzahlen.

job_stop — einen laufenden Job stoppen

job_stop(job_id="job_...", signal="terminate", kill_after_seconds=10)

Beendet den Prozessbaum des Jobs. Zuerst wird ein sanftes signal (Standard terminate) gesendet; wenn der Job nicht innerhalb von kill_after_seconds beendet wurde, wird er gekillt. Der Job wird erst dann cancelled, wenn der Arbeitslastbaum tatsächlich beendet wurde; andernfalls bleibt er running und der Stopp ist wiederholbar.

job_mark_delivery / job_retry_delivery — manuelle Zustellungskontrolle

job_mark_delivery(delivery_id="del_...", status="delivered", error="optional reason")
job_retry_delivery(delivery_id="del_...")   # requeue a failed delivery

job_mark_delivery setzt den Status einer Zustellung manuell (z.B. nach Behebung eines Adapterproblems); job_retry_delivery stellt eine fehlgeschlagene Zustellung für den nächsten Versanddurchlauf erneut in die Warteschlange. job_delivery_attempts zeigt die Claim/Lease-Historie.

job_cleanup — alte terminale Jobs entfernen

job_cleanup(older_than_seconds=86400, dry_run=true)   # preview
job_cleanup(older_than_seconds=86400, dry_run=false)  # delete

Entfernt terminale Jobs, die älter als der Grenzwert sind: Logs, Ereignisspiegel, Spezifikationen, Zustellungen, Versuche, Wake-Ziele, Ereignisse, dann die Job-Zeile. Laufende Jobs werden nie ausgewählt. Der Trockenlauf ist vollständig schreibgeschützt. Die Bereinigung kann sicher wiederholt werden.

job_metrics_query — gespeicherte skalare Reihen lesen

job_metrics_query(job_id="job_...", metric="loss", from_ms=..., to_ms=..., limit=1000)

Gibt die gespeicherten Reihen für einen Job zurück, gruppiert nach Metrikname. metric filtert auf eine einzelne Reihe (z.B. loss, acc, progress.percent); from_ms/to_ms filtern nach Ereigniszeitstempel (Epochen-Millisekunden). Punkte sind nach Ereignissequenz geordnet. Dies ist die Leseseite der Daten des Terminal-Monitors.

job_metric_compare — eine Metrik über Läufe hinweg vergleichen

job_metric_compare(job_ids=["job_a", "job_b"], metric="val_loss", aggregation="min")

Vergleicht eine Metrik über Jobs hinweg (z.B. val_loss über Seeds oder Konfigurationen). aggregation ist latest, mean, min, max, sum oder count; das Ergebnis enthält den Wert pro Job plus die ersten/letzten Punkte. Dies ist das W&B-artige „Welcher Lauf hat gewonnen?"-Primitiv.

job_run_summary — hat es funktioniert?

job_run_summary(job_id="job_...")

Ein Aufruf gibt Status, Name, Laufzeit, Exit-Code, aktuellsten Fortschritt, Notizen, eine Übersicht pro Metrik (aktuellster/erster/min/max/Anzahl) und angehängte Artefakte zurück – der schnellste Weg für einen Agenten, über einen abgeschlossenen Job zu berichten.

job_artifact_add / job_artifacts — Ausgaben anhängen

job_artifact_add(job_id="job_...", name="best.pt", uri="file:///...", kind="checkpoint",
                 size_bytes=..., sha256="...", meta={"epoch": 5})
job_artifacts(job_id="job_...")

Hängt Artefakte (Checkpoints, CSVs, gerenderte Ausgaben) an einen Job an, sodass sie in job_run_summary aufgelistet und später abrufbar sind. meta ist frei formatiertes JSON.

job_dashboard — Diagrammdaten für jeden Renderer

job_dashboard(job_ids=["job_..."], limit=5000)

Gibt die Jobliste plus jede gespeicherte Metrikreihe zurück, heruntergesampelt auf limit Punkte pro Reihe – dieselben Daten, die der Go-Terminal-Monitor diagrammiert, bereitgestellt über HTTP/MCP, sodass jeder Client (ein zukünftiges Web-/Cloud-Dashboard) sie rendern kann.


Wake-Ziele (einen Agenten wecken, wenn ein Job Aufmerksamkeit benötigt)

Wenn ein Job ein passendes Ereignis auslöst, erstellt der Daemon eine dauerhafte Zustellung und sendet sie über den Adapter. Die Zustellung erfolgt mindestens einmal; jede Nutzlast trägt eine delivery_id zur Deduplizierung.

local_command

Führt einen beliebigen Befehl aus und übergibt die Zustellungsnutzlast als JSON auf stdin:

{
  "type": "local_command",
  "events": ["checkpoint", "failed", "completed"],
  "command": ["python", "deliver.py"]
}

Exit 0 markiert die Zustellung als delivered; jeder andere Exit markiert sie als failed.

codex_thread

Setzt einen Codex-Thread über den lokalen App-Server fort:

{
  "type": "codex_thread",
  "thread_id": "019f...",
  "events": ["checkpoint", "failed", "completed"],
  "codex_command": ["C:\\codex\\codex.exe"]
}

Protokoll: initialize -> thread/resume -> turn/start.

opencode_thread

Setzt eine OpenCode-Sitzung fort:

{
  "type": "opencode_thread",
  "thread_id": "ses_...",
  "events": ["checkpoint", "failed", "completed"],
  "cwd": "F:\\git\\project",
  "opencode_command": ["opencode"],     # override the binary
  "attach": "http://127.0.0.1:4096",    # submit via an opencode serve instance
  "timeout_seconds": 120
}

Das Standard-Timeout für OpenCode-Turns beträgt 30 Sekunden; erhöhe es für lange Turns.

Gemeinsame Zustellungsoptionen

{
  "type": "codex_thread",
  "thread_id": "019f...",
  "events": ["checkpoint"],
  "auto_dispatch": false,      // leave the delivery pending for manual inspection
  "max_attempts": 3,           // default 1
  "retry_delay_seconds": 5,    // default 5
  "timeout_seconds": 30        // adapter timeout; also sizes the delivery lease
}

Mit auto_dispatch: false bleiben Zustellungen pending, bis ein Agent sie entweder manuell versendet oder das Ziel ändert.

Zustellungsoperationen

job_deliveries(job_id="job_...")
job_delivery_attempts(delivery_id="del_...")
job_retry_delivery(delivery_id="del_...")     # requeue a failed delivery
job_mark_delivery(delivery_id="del_...", status="delivered")

Die Versuchshistorie zeichnet den Claim-Token, Start-/Endzeiten, Status und ob der Versuch nach Ablauf eines Leases zurückgefordert wurde. Wenn der Daemon abstürzt, nachdem ein Adapter einen Wake akzeptiert hat, aber bevor Vanth den Erfolg aufzeichnet, wird die Zustellung zurückgefordert und erneut versucht – als reclaimed-Versuch angezeigt, anstatt als Exactly-Once-Zustellung beansprucht.


Den Daemon ausführen

Vordergrund (für Entwicklung oder Diagnose):

uv run vanthd

Optionen für den Start bei der Anmeldung:

  • Windows: Der Daemon wird aus dem Benutzer-Startup-Ordner (startup_commands.bat) zusammen mit anderen Startbefehlen gestartet; eine Taskplaner-Aktionsvorlage befindet sich ebenfalls in deploy/vanthd.cmd.

  • Unix: deploy/vanthd.service ist ein systemd-Benutzerdienst.

Aktiviere nur einen Daemon pro VANTH_HOME. Ein zweiter Daemon für dasselbe Home beendet sich sofort (OS-Level-Sperre). Der Daemon bindet nur an Loopback (127.0.0.1 / ::1 / localhost); ein Nicht-Loopback-VANTH_DAEMON_HOST wird abgelehnt.

Sicherheit

  • Jeder Datenpfad erfordert Authorization: Bearer <token>; der Token wird pro Home generiert und nie protokolliert. GET /health ist die einzige nicht authentifizierte Route (eine günstige Lebendigkeitssonde für Supervisoren).

  • Beim Daemon-Start wird das Zustandsverzeichnis erneut auf den Eigentümer eingeschränkt: Unix chmod 0700/0600; Windows deaktiviert die ACL-Vererbung und gewährt nur dem Eigentümer, SYSTEM und Administratoren über icacls Zugriff. Dies blockiert andere Konten (z.B. Sandbox/CI-Benutzer, die Leseberechtigung vom Benutzerprofil erben) daran, den Token oder die Umgebungs-/Spezifikationsdaten pro Job zu lesen.

  • Unter Windows ist der Socket SO_REUSEADDR deaktiviert, sodass ein zweiter Daemon nicht zu einem Phantom-Listener auf demselben Port werden kann; ein fehlgeschlagenes Bind gibt die Home-Sperre frei und beendet sich sauber.

Der Go-Terminal-Monitor

Das native Go-Dashboard liest dasselbe Home schreibgeschützt und rendert Live-Diagramme, Fortschrittsbalken, die genaue Ereignistabelle und Log-Enden:

uv run vanth-monitor

Von einem gebauten Wheel aus führt vanth-monitor die gebündelte native Binärdatei aus (kein Go-Toolchain erforderlich). Von einem Source-Checkout aus baut es den Monitor bei der ersten Verwendung und speichert ihn unter ~/.cache/vanth/ zwischen (erfordert go im PATH):

go build -o bin\vanth.exe ./cmd\vanth
bin\vanth.exe monitor

Tasten: up/down oder j/k wählen Jobs aus · enter fixiert die Reihe eines Jobs · e Ereignistabelle · l Log-Ende · +/- Diagramm zoomen · [/] schwenken · t zurück zum Live-Ende · ? Hilfe · q oder Ctrl+C beenden.


Konfigurationsreferenz

Umgebungsvariablen (Standardeinstellungen befinden sich in src/vanth/server.py, src/vanth/daemon.py, src/vanth/migrations.py):

Variable

Standard

Zweck

VANTH_HOME

~/.vanth

Zustandsstammverzeichnis (Alias: AGENT_BG_HOME)

VANTH_DAEMON_URL

http://127.0.0.1:8765

Wo Clients den Daemon erreichen

VANTH_DAEMON_HOST

127.0.0.1

Bind-Adresse (nur Loopback)

VANTH_DAEMON_PORT

8765

Bind-Port

VANTH_MAX_REQUEST_BYTES

1 MiB

HTTP-Anfragekörper-Obergrenze

VANTH_MAX_RESPONSE_BYTES

4 MiB

HTTP-Antwort-Obergrenze

VANTH_MAX_EVENT_BYTES

64 KiB

Einzelereignis-Nutzlast-Obergrenze

VANTH_MAX_EVENT_LINE_BYTES

1 MiB

AGENT_EVENT-Zeilen-Obergrenze

VANTH_MAX_LOG_BYTES

10 MiB

Pro-Stream-Log-Obergrenze (Drain läuft weiter)

VANTH_MAX_EVENTS_PER_JOB

100000

Strukturierte Ereignis-Obergrenze pro Job

VANTH_DELIVERY_POLL_INTERVAL

0.2s

Wartungsschleifen-Takt

VANTH_DELIVERY_LEASE_MARGIN

5s

Zusätzliche Lease-Zeit über Adapter-Timeout hinaus

VANTH_RUNNER_HEARTBEAT_INTERVAL

1s

Lebendigkeits-Herzschlag des Runners

VANTH_RUNNER_HEARTBEAT_STALE_AFTER

10s

Herzschlag-Veraltungsschwelle

VANTH_CODEX_BIN

codex / C:\codex\codex.exe

Codex-Binärdatei

VANTH_OPENCODE_BIN

opencode (via shutil.which)

OpenCode-Binärdatei

VANTH_LOG_LEVEL

INFO

Daemon-Log-Level

VANTH_LOG_MAX_BYTES

5 MiB

Rotierende Daemon-Log-Größe

VANTH_LOG_BACKUP_COUNT

3

Daemon-Log-Rotationsanzahl

VANTH_BUSY_TIMEOUT_MS

30000

SQLite-Schreibsperr-Wartezeit


Betrieb

Zustandslayout

~/.vanth/
  jobs.sqlite      durable jobs (incl. env, notes, run-overview) / events / deliveries / targets / attempts / tombstones
  token            bearer token (owner-only permissions)
  daemon.lock      single-daemon OS lock
  daemon.json      discovery metadata (url, pid, started_at, schema) — written atomically, removed on graceful shutdown
  logs/            daemon.log + per-job runner/stdout/stderr logs
  events/          per-job JSONL event mirrors (monitor fallback source)
  specs/           per-job launch specs (removed once the runner starts)
  backups/         pre-migration SQLite backups

Gesundheit, Bereitschaft und Diagnose

job_doctor()

Meldet das Zustandsverzeichnis, Datenbanktabellen, Zustellungsanzahlen nach Status, Schema-Version, PRAGMA quick_check, veraltete Zustellungs-Leases, freien Speicherplatz, Token-Pfad und ob die Codex/OpenCode-Binärdateien aufgelöst werden. Es gibt niemals den Token preis.

Der HTTP-Daemon stellt außerdem bereit:

  • GET /health — günstige, nicht authentifizierte Lebendigkeitssonde für Supervisoren;

  • GET /ready — authentifizierte Bereitschaft (Arztbericht; 503, wenn nicht in Ordnung).

Upgrades und Backups

Schemaänderungen sind geordnete SQLite-Migrationen. Vor der ersten Migration einer vorhandenen Datenbank wird ein zeitgestempeltes Backup unter backups/ über die SQLite-Backup-API geschrieben (niemals eine rohe Dateikopie, während WAL aktiv ist). Um manuell zu aktualisieren, kopiere zuerst das neueste backups/*.sqlite. Ein zukünftiges Datenbankschema wird abgelehnt, ohne die Dateien zu berühren.


HTTP-API (Äquivalent der MCP-Tools)

Authentifiziert mit Authorization: Bearer <token>.

Methode

Pfad

Zweck

GET

/jobs

Jobs auflisten (status, limit, thread_id, name, tags)

POST

/jobs

Einen Job starten

POST

/jobs/{id}/rerun

Einen Job mit seiner ursprünglichen Konfiguration erneut ausführen

GET

/jobs/{id}/status

Job-Status (enthält Befehl/Umgebung/Arbeitsverzeichnis)

GET

/jobs/{id}/events

Ereignisse (since_event_id, types, limit, reverse)

GET

/jobs/{id}/metrics

Metrikreihen (metric, from_ms, to_ms, limit)

GET

/jobs/{id}/summary

Ausführungszusammenfassung (Status, Laufzeit, Metriken, Artefakte)

GET

/jobs/{id}/artifacts

Artefakte (limit)

POST

/jobs/{id}/artifacts

Ein Artefakt hinzufügen

GET

/metrics/compare

Metrik über Jobs hinweg vergleichen (job_ids, metric, aggregation)

GET

/dashboard

Diagrammdaten (job_ids, limit)

GET

/jobs/{id}/tail

Log-Ende (stream, max_bytes, offset)

POST

/jobs/{id}/wait

Auf ein Ereignis warten

POST

/jobs/{id}/stop

Einen Job stoppen

GET

/view

Agentenansicht (thread_id, limit)

GET

/deliveries

Zustellungen (job_id, status, limit)

GET

/deliveries/{id}/attempts

Versuchshistorie

POST

/deliveries/{id}/mark

Eine Zustellung markieren

POST

/deliveries/{id}/retry

Eine Zustellung wiederholen

POST

/cleanup

Bereinigung (older_than_seconds, dry_run)

GET

/doctor

Gesundheitsbericht

GET

/health

Nicht authentifizierte Lebendprüfung


Tipps zur Agentennutzung

  1. Warten, nicht abfragen. Verwenden Sie job_wait(job_id, filters=[...], timeout_seconds=...) anstatt job_status in einer Schleife. Der Daemon weckt den Wartenden sofort, sobald ein passendes Ereignis gespeichert wird.

  2. Übergeben Sie since_event_id an den nächsten job_wait, nachdem Sie ein Ereignis verarbeitet haben, damit Sie nie ein altes erneut verarbeiten.

  3. Taggen und Threaden Sie Ihre Jobs. Setzen Sie origin_thread_id (den Agenten-Thread, der den Job gestartet hat) und tags; verwenden Sie job_view(thread_id=...) zur Zusammenfassung.

  4. Bevorzugen Sie job_view gegenüber job_status, wenn Sie einem Benutzer eine Situation präsentieren – es ist bereits nach Aufmerksamkeitspriorität sortiert.

  5. Machen Sie Jobs selbsterklärend. Geben Sie AGENT_EVENT progress / checkpoint / metric-Zeilen aus (siehe oben). Jobs, die still sind, funktionieren zwar auch, aber getrackte Jobs sind viel einfacher nachzuvollziehen.

  6. Verwenden Sie Weckziele für lange Jobs. Wenn ein Trainingslauf oder ein langer Download eine Entscheidung an einem Checkpoint benötigt, fügen Sie ein codex_thread- oder opencode_thread-Ziel mit events: ["checkpoint", "failed", "completed"] hinzu, damit der Agent fortgesetzt wird, anstatt abzufragen.

  7. Überprüfen Sie Zustellungsfehler. job_delivery_attempts zeigt die Lease-/Claim-Historie; job_retry_delivery stellt einen fehlgeschlagenen nach Behebung der Ursache wieder in die Warteschlange.

  8. Setzen Sie ein sinnvolles timeout_seconds bei job_start, damit ein hängender Befehl in den timeout-Zustand (terminal) übergeht, anstatt ewig zu laufen; der Runner erzwingt dies sogar über Daemon-Neustarts hinweg.

  9. Räumen Sie alte Zustände auf mit job_cleanup(older_than_seconds=..., dry_run=false), damit der SQLite-Speicher und die Logdateien begrenzt bleiben.

  10. Führen Sie fehlgeschlagene Jobs erneut aus, bauen Sie sie nicht neu. job_rerun(job_id=...) startet mit dem ursprünglichen Befehl, der Umgebung, dem Arbeitsverzeichnis und den Weckzielen neu – ideal, um einen vorübergehend fehlgeschlagenen Download oder Batch zu wiederholen.

  11. Fragen Sie „Was ist das für ein Job?“ mit job_status. Es gibt jetzt den Befehl, das Arbeitsverzeichnis, die Umgebung und das Timeout zurück, sodass Sie einem Benutzer einen Job erklären können, ohne Logs zu lesen.

  12. Filtern Sie Listen nach Name/Tag. job_list(name="train", tags=["gpu"]) grenzt eine wachsende Jobliste ein, ohne alles durchzublättern.

  13. Verwenden Sie reverse=true für „Was ist kürzlich passiert?“. job_events(job_id, reverse=true, limit=20) gibt die neuesten Ereignisse zuerst zurück, und Sie können mit since_event_id auf die älteste gesehene ID weiter zurückblättern.

  14. Ein Job überlebt den Daemon. Der Runner ist abgekoppelt; Jobs laufen über Daemon-/MCP-Neustarts hinweg weiter. Wenn ein Runner bei der Wiederherstellung fehlt, wird der Job als orphaned markiert (niemals stillschweigend verworfen).


Beispiele

uv run python examples\long_job.py    # emits progress + checkpoints

examples/long_job.py ist ein kleiner Referenzjob, der vanth.agent_events verwendet. Starten Sie ihn über job_start und beobachten Sie ihn in vanth monitor.


Fehlerbehebung

  • Unauthorized (401): Das Bearer-Token in ~/.vanth/token ist das, was der Daemon erwartet. Stellen Sie sicher, dass VANTH_HOME für Daemon und Client gleich ist.

  • Zweiter Daemon startet nicht: another vanthd already owns this VANTH_HOME. Ein Daemon pro Home, wie vorgesehen.

  • Job hängt in running und wird dann orphaned: Der Runner-Prozess ist gestorben. Überprüfen Sie logs/<job_id>.runner.log und die Heartbeat-Schwellenwerte.

  • Keine Diagramme im Monitor: Der Job gibt keine AGENT_EVENT metric- oder progress-Zeilen aus – fügen Sie sie hinzu (optional).

  • OpenCode-Weck-Timeout: Erhöhen Sie timeout_seconds im Weckziel über die erwartete Bearbeitungsdauer hinaus.

  • Monitor zeigt nichts / leeren Zustand: Stellen Sie sicher, dass VANTH_HOME auf das Home des Daemons zeigt und dass dort jobs.sqlite existiert.


Entwicklung

uv run pytest -q                 # Python suite (112 passed, 1 Linux-only skip)
uv run python -m compileall -q src tests examples
uv build                         # sdist + wheel; wheel bundles the Go monitor
go vet ./... && go test ./...    # Go: config, state, monitor

Der Wheel-Build führt einen Hatchling-Build-Hook (build-hooks/bundle_monitor.py) aus, der den Go-Monitor für die Host-Plattform kompiliert und unter vanth/monitor-bin/ bündelt, sodass vanth-monitor zur Laufzeit keine Go-Toolchain benötigt. go muss beim Bauen des Wheels im PATH sein; zum Installieren oder Ausführen wird es nicht benötigt. Wheels sind plattformgetaggt (py3-none-<platform>), da sie das native Binary enthalten.

Die Release-Gate-Automatisierung befindet sich in scripts/:

  • scripts/chaos_matrix.py – schwere synthetische Workloads und Kill-/Restart-Matrix;

  • scripts/real_adapter_smoke.py – optionale Live-Codex/OpenCode-Weck-Smokes (setzen Sie VANTH_SMOKE_CODEX_THREAD / VANTH_SMOKE_OPENCODE_SESSION);

  • scripts/generate_go_fixture.py – generiert die deterministische Schema-v5- Konformitäts-Fixtur in testdata/ neu;

  • scripts/demo_jobs.py – startet Demo-Jobs (Trainingslauf, schnelle Aufgabe, fehlschlagende Aufgabe) für den Monitor.

Einschränkungen (v1)

  • Interaktives stdin und job_send sind nicht implementiert; Jobs laufen mit geschlossenem stdin (verwenden Sie nicht-interaktive Flags bei Befehlen).

  • Die Zustellung erfolgt mindestens einmal; ein Absturz, nachdem ein Adapter einen Weckruf akzeptiert hat, aber bevor Vanth den Erfolg aufgezeichnet hat, ist eine dokumentierte, offengelegte Mehrdeutigkeit.

  • Fernzugriff, TLS, Mehrbenutzerrichtlinien, Kontingente, verteilte Worker und ein benutzerdefinierter Dienstmanager sind nicht im Umfang enthalten.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Git-backed platform for skills, tools, and context for AI agents

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

  • Reliable async execution for agent tool calls: schema gating, retries, idempotency, audit trail.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/abhim-dv/vanth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server