Commit Graph
26 Commits
Author SHA1 Message Date
ARIA 128c11b49d debug(proxy): Streaming-close/result-Events sichtbar loggen statt still leer zu bleiben
Root Cause fuer 'Empty response (no content or reasoning)'-Retries im
hermes-agent-Log noch nicht zweifelsfrei geklaert -- der bisherige Code
konnte einen Subprocess-Abschluss ohne 'result'-Event (code===0, kein
Fehler-Chunk) still mit nur [DONE] beenden, was beim Client exakt wie eine
leere Antwort aussieht. Jetzt: immer ein sichtbarer Fehler-Chunk + Log-Zeile
bei fehlendem result-Event, und ein Debug-Log bei jedem result-Event mit
subtype/is_error/Textlaenge. Naechster Repro-Versuch mit docker logs
hermes-proxy zeigt damit die echte Ursache.
2026-07-20 07:59:29 +00:00
ARIAandClaude Sonnet 5 4710e4071b fix(proxy): Streaming-Pfad parst <tool_call>-Tags nie -> Tool-Calls wirkungslos
Root Cause (verifiziert am echten npm-Paket claude-max-api-proxy@dist):
handleStreamingResponse() in server/routes.js baut SSE-Chunks komplett
inline aus den rohen content_delta-Tokens und ruft dabei NIE den
<tool_call name="X">{json}</tool_call>-Parser aus adapter/cli-to-openai.js
auf (_parseToolCalls / cliResultToOpenai) -- der laeuft bisher nur im
Non-Streaming-Pfad (handleNonStreamingResponse). Hermes fragt aber immer
mit stream:true an. Folge: Claude gibt die Tool-Call-Tags korrekt als Text
aus (das hatten wir zuletzt gefixt), aber sie kommen nie als echtes OpenAI
tool_calls[]-Array bei Hermes an -- kein Tool wird ausgefuehrt, kein
Ergebnis geht zurueck in die Session, Claude haeuft neue <tool_call>-
Bloecke an weil nie eine Antwort kommt ("es passiert nicht viel", vier
Tags in einer Antwort).

Bisher gab's fuer routes.js nur einen minimalen sed-Patch (systemPrompt
durchreichen), bewusst ohne ARIAs vollen routes.js-Patch (der haengt an
ARIA-Bridge-spezifischen Extras wie Killswitch/Live-Stream). Der Fehler
sitzt aber strukturell in handleStreamingResponse selbst, sed reicht nicht
mehr.

Fix: proxy-patches/routes.js -- eigene schlanke Variante ohne Bridge-
Extras, komplett per cp ersetzt statt sed. handleStreamingResponse
streamt keine rohen Tokens mehr live, sondern wartet auf das volle
"result"-Event und baut daraus per cliResultToOpenai() (dieselbe Logik
wie Non-Streaming) den finalen Chunk inkl. tool_calls[]/finish_reason.
Kostet den Live-Tipp-Effekt, aber Tool-Calls funktionieren jetzt
ueberhaupt erst. systemPrompt-Fix ist in der neuen Datei bereits
enthalten (ersetzt den alten sed dafuer).

Lokal verifiziert: node --check auf der neuen Datei, und
cliResultToOpenai() gegen Stefans exakten Beispiel-Text (vier
zusammenhaengende <tool_call>-Tags) getestet -- liefert korrekt 4
tool_calls + finish_reason=tool_calls.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 07:55:55 +00:00
ARIA e00b406e23 fix(proxy): Claude explizit warnen, injizierte Tools NICHT nativ/via ToolSearch aufzurufen
"No such tool available" + Session-Neustart-Vorschlag trotz aktivem Toolset
(Verbindungstest komplett gruen). Ursache: Claude Code CLI versucht die im
System-Prompt nur als TEXT beschriebenen Tools (spotify_*, ...) ueber sein
eigenes natives Funktionsaufruf-/ToolSearch-System zu finden/aufzurufen,
weil das "# Verfuegbare Tools"-Listenformat wie eine echte Tool-Registrierung
aussieht. Da nichts davon ueber die Anthropic-API als tools-Parameter
registriert ist (nur Prompt-Text), schlaegt das immer fehl -> Claude
interpretiert den Fehler faelschlich als "nicht verbunden/nicht geladen".

Fix: expliziter Warnabsatz im Tool-Block, der genau das verbietet und klar
macht dass "No such tool available" ein Bedienfehler (falsches Aufrufmuster)
ist, kein Verbindungsproblem. Analog zu ARIAs eigenem System-Prompt, der
diese Klarstellung schon enthaelt.
2026-07-20 00:34:25 +00:00
ARIA f8d71e00bb fix(proxy): Claude anweisen, Tool-Verbindungsstatus nicht zu halluzinieren
Live-Test (spotify_test_connection.py) bestaetigte: Login, Toolset und
API-Erreichbarkeit sind alle OK. Trotzdem antwortete Claude im Chat
'Tools in Werkzeugliste beschrieben, aber nicht verbunden/aktiv' - eine
frei erfundene Einschraenkung, die nirgends im injizierten System-Prompt
stand (der beschreibt nur Namen/Schema der Tools, nichts zum Verbindungs-
status). Klassisches Modell-Hedging bei Unsicherheit ueber Backend-Status.

Ergaenzt im Tool-Block: expliziter Hinweis dass alle gelisteten Tools
bereits authentifiziert/einsatzbereit sind und ohne Rueckfrage zur
Verbindung genutzt werden sollen, Fehler nur anhand echter Tool-Antworten
(z.B. 401/403) melden.
2026-07-20 00:27:22 +00:00
ARIA d05ab23c1b Spotify: read-only Verbindungstest-Skript (Toolset/Login/Live-API), analog zum Auth-Skript
Prueft direkt in Hermes' eigenem Code (get_auth_status, _get_platform_tools,
SpotifyClient), ob Toolset aktiviert, Login vorhanden und ein echter
GET /me/player/devices erfolgreich ist -- ohne Umweg ueber Claude/Proxy,
da LLM-Textantworten wie 'nicht verbunden/aktiv' kein verlaesslicher
Diagnosewert sind.
2026-07-20 00:21:20 +00:00
ARIA 8f3a07adb4 fix: routes.js reicht cliInput.systemPrompt nie an subprocess.start() durch
Root Cause fuer "Claude kennt Hermes-Tools nicht" gefunden: unser
openai-to-cli.js baut systemPrompt korrekt, manager.js liest options.systemPrompt
korrekt fuer --system-prompt -- aber der stock npm-Code in server/routes.js
kopiert beim subprocess.start(cliInput.prompt, {model, sessionId}) nur model+
sessionId, nie systemPrompt. Also war options.systemPrompt immer undefined,
--system-prompt kam nie mit echtem Inhalt an, Claude lief die ganze Zeit mit
seiner eigenen Default-Identitaet statt der injizierten Hermes-Tools.

Neuer sed patcht jetzt auch routes.js und reicht systemPrompt durch (trifft
beide Call-Sites: Streaming + Non-Streaming). Verifiziert lokal gegen das
echte npm-Paket claude-max-api-proxy@1.0.0.
2026-07-20 00:15:39 +00:00
ARIA 5c5391b16e Debug: temp. Logging in openai-to-cli.js fuer Root-Cause 'Claude kennt Hermes-Tools nicht'
Verifiziert (lokal): claude --system-prompt "" faellt komplett auf die eigene
Default-Identitaet zurueck (eigene native Tools, eigene Account-Connectors) statt
mit leerem Prompt zu laufen. Das erklaert exakt das beobachtete Symptom bei Hermes
(Claude nennt Gmail/Calendar/Drive + Cron/Task, kennt aber keinerlei Spotify-Tool).

Hypothese: extractSystemPrompt(request.messages, request.tools) liefert fuer
Hermes' Requests einen leeren/zu duennen String -- entweder weil Hermes gar kein
tools-Feld mitschickt (Spotify-Toolset in hermes tools nie aktiviert?) oder weil
keine system-Message im erwarteten Format ankommt. Logging zeigt messageRoles,
toolsCount, toolNames und systemPromptLen/-Preview in docker logs hermes-proxy --
damit klaeren wir das anhand echter Daten statt weiter zu raten.
2026-07-20 00:04:09 +00:00
ARIA c2e920f7d6 Fix: Claude nutzte eigene native Skills/MCP-Connectors statt injizierter Hermes-Tools
hermes-proxy laeuft mit echter Claude-Code-CLI-Session auf ggf. demselben
Anthropic-Account wie ARIA -> account-weite Connectors (Gmail/Calendar/Drive)
und Claude Codes eigenes Skill-System wurden automatisch mitgeladen und
haben mit den per Prompt injizierten <tool_call>-Tools (Hermes-Plugins wie
Spotify) konkurriert. --safe-mode deaktiviert alle nativen Customizations
fuer diese Session, sodass nur noch die injizierten externen Tools sichtbar
sind.
2026-07-19 23:56:37 +00:00
ARIA cb7bd566e6 Spotify: manueller PKCE-Login ohne SSH-Port-Forward (Redirect-URI ist hart auf localhost validiert) 2026-07-19 23:33:57 +00:00
ARIA f448538158 Doku: Toolsets (Spotify etc.) sind Opt-in, hermes tools + hermes auth noetig 2026-07-19 23:25:44 +00:00
ARIA 7b8bcb0b04 Fix: WS-Chat/Events-Feed brach durch Origin-Header-Mismatch ab (Caddyfile)
Root Cause im echten Hermes-Sourcecode (hermes_cli/web_server.py,
_ws_host_origin_reason) verifiziert, nicht geraten: WebSocket-Upgrades
(/api/ws, /api/pub, /api/events) haben einen zweiten Guard neben dem
Host-Header, der zusaetzlich den Origin-Header gegen den Bind (127.0.0.1)
prueft. Browser schickt die echte Server-Adresse als Origin -> origin_mismatch
-> WS wird mit 4403 (Browser: 1006) geschlossen, komplett ohne Logging
(anders als /api/console + /api/pty). Fix: Origin-Header in Caddy entfernen,
dann ueberspringt Hermes den Check ganz.
2026-07-19 23:10:29 +00:00
ARIA 367ff648e8 Gateway+Dashboard in einen Container zusammenfassen (HERMES_DASHBOARD=1)
hermes-agent-dashboard als separater Container ist raus. Stattdessen
supervised s6 Gateway und Dashboard jetzt als Geschwister-Prozesse im
selben hermes-agent-Container (HERMES_DASHBOARD=1 + HERMES_DASHBOARD_HOST=
127.0.0.1) -- laut Startup-Log selbst "the recommended setup for the s6
container image". Das ist der wahrscheinliche Fix fuer zwei offene Bugs:
Dashboard zeigte Gateway-Status immer als "Stopped" (lokale Prozess-
Inspektion sah den Gateway-Prozess im getrennten Container nie) und der
Restart-Button warf "no such gateway 'default'" (loeste s6-Services nur im
eigenen Container auf). README + Architektur-Diagramm entsprechend
aktualisiert.
2026-07-19 23:03:00 +00:00
ARIA 804b5192fc Doku: Restart-Gateway-Button-Fehler ('no such gateway default') erklaeren und Workaround dokumentieren 2026-07-19 22:58:00 +00:00
ARIA 4be51faa07 Caddy: Host-Header beim Reverse-Proxy auf 127.0.0.1:9119 setzen (Fix Invalid Host header)
Hermes' Dashboard-Backend prueft den eingehenden Host-Header gegen den
Host, an den es gebunden ist -- Caddy reichte bisher den originalen
Host-Header vom Browser (Server-IP/Domain) durch, was Hermes als
'Invalid Host header' ablehnte. header_up ueberschreibt den Host jetzt
explizit. Plus Troubleshooting-Eintrag in der README.
2026-07-19 22:37:38 +00:00
ARIA 3aaddc4a31 Dashboard-Auth komplett entfernt statt nur optional: Caddy ist einzige Auth-Schicht
Hermes' eigenes Auth-Gate (dashboard.basic_auth/HERMES_DASHBOARD_PASSWORD_HASH)
engagiert sich laut eigener Fehlermeldung ohnehin nur bei einem 0.0.0.0-Bind,
der Dashboard-Container bindet aber fest auf 127.0.0.1 -- das Gate haette hier
nie gegriffen. Statt es als "Fallback falls Bind mal wieder wandert" zu
behalten, jetzt sauber raus: HERMES_DASHBOARD_PASSWORD_HASH aus .env.example
und docker-compose.yml, der auskommentierte dashboard:-Block aus
config.yaml.example, README-Setup auf 8 statt 9 Schritte umnummeriert.
Caddy (TLS + eigene Basic-Auth) ist damit die einzige noetige Auth-Schicht --
ein Layer statt zwei, weniger Setup, weniger Verwirrung. Stale Kommentare im
Caddy-Compose-Block (redeten noch von "zweiter Basic-Auth-Schicht noetig weil
unklar ob dashboard.basic_auth greift") ebenfalls korrigiert.
2026-07-19 22:23:22 +00:00
ARIA b1423ffb6e Dashboard-Auth: Hermes-eigene Auth nicht mehr Pflicht, Caddy reicht
Dashboard bindet seit dem Caddy-Umbau nur noch auf 127.0.0.1 -- Hermes'
Auth-Gate engagiert sich laut eigener Fehlermeldung NUR bei einem
0.0.0.0-Bind, greift also gar nicht mehr. HERMES_DASHBOARD_PASSWORD_HASH
ist damit optional statt Pflicht (kein docker-compose-Abbruch mehr bei
leerem Wert), der dashboard: basic_auth-Block in config.yaml.example ist
standardmaessig auskommentiert. Caddy (TLS + eigene Basic-Auth) bleibt
die einzige im Normalbetrieb noetige Auth-Schicht. README/.env.example
entsprechend entschlackt -- Hash-Erzeugen + Dollarzeichen-Escaping nur
noch als Fallback dokumentiert, falls der Bind mal wieder auf 0.0.0.0
wandert.
2026-07-19 22:15:26 +00:00
ARIA 68ca43e5ac Caddy: Zertifikat wird automatisch im Container erzeugt statt manuell per openssl auf dem Host
Entrypoint im caddy-Service prueft beim Start ob /certs/dashboard.{crt,key}
schon existiert und erzeugt es sonst selbst (apk add openssl + req -x509,
100 Jahre). Persistent im Volume, kein manueller Schritt mehr noetig.
Manuelle Erzeugung bleibt als Fallback in der README dokumentiert (z.B.
fuer eigenes /CN).
2026-07-19 22:11:49 +00:00
ARIA 66bcd0dfb1 Dashboard hinter Caddy: TLS (100 Jahre selbstsigniert) + eigene Basic-Auth auf Port 443
Stefans Entscheidung: 0.0.0.0-Bind mit unverschluesseltem Basic-Auth war nur
Testzustand. Jetzt bindet hermes-agent-dashboard wieder auf 127.0.0.1, ein
neuer Caddy-Service (network_mode: host) terminiert TLS auf 443 mit einem
manuell erzeugten selbstsignierten Zertifikat (100 Jahre Laufzeit, kein
Let's-Encrypt-Domain-Zwang) und erzwingt eine zweite, eigene Basic-Auth-Schicht
(bcrypt-Hash in CADDY_BASIC_AUTH_HASH, getrennt von Hermes' pbkdf2-Hash) -
noetig weil Hermes' eigenes Auth-Gate laut Fehlermeldung nur bei 0.0.0.0-Binds
"engagiert". README + .env.example um die Setup-Schritte (Cert-Erzeugung,
Caddy-Hash, $-Escaping-Falle) ergaenzt.
2026-07-19 22:07:17 +00:00
ARIA 24e09a46ce README: sed-Befehl fuers Hash-Escaping ergaenzen + wahrscheinliche Root Cause dokumentieren (dashboard: Block fehlt in bereits existierender config.yaml, da Schritt 3 nur einmalig kopiert) 2026-07-19 22:02:58 +00:00
ARIA 8b9aa73f98 Doku: Dollarzeichen-Falle beim Passwort-Hash in .env dokumentieren
docker-compose interpretiert rohe $-Zeichen im pbkdf2-Hash (.env) selbst
als Variablen-Referenzen und ersetzt sie stillschweigend durch Leerstring
(sichtbar als "WARN variable is not set"). Ergebnis: verstümmelter Hash,
Dashboard bleibt trotz "richtig" eingetragener Werte bei "no auth
providers are registered" haengen. Fix: jedes $ im Hash durch $$ ersetzen.
2026-07-19 21:52:37 +00:00
ARIA 68bdc0968f README: fehlenden Dashboard-Auth-Setup-Schritt ergaenzen + Henne-Ei-Bug fixen
README dokumentierte den seit 19.07.2026 pflichtigen Dashboard-Auth-Block
(HERMES_DASHBOARD_USER/PASSWORD_HASH, dashboard.basic_auth in config.yaml)
noch gar nicht - neuer Setup-Schritt 4, Schritt 7/8 umnummeriert, Architektur-
Diagramm und Sicherheitshinweis auf den 0.0.0.0-Dashboard-Stand gebracht.

Nebenbei echten Bug gefunden: .env.example und config.yaml.example sagten,
den Passwort-Hash per 'docker exec hermes-agent-dashboard' zu erzeugen -
aber genau dieser Container startet ohne bereits gesetztes
HERMES_DASHBOARD_PASSWORD_HASH gar nicht (docker-compose :?-Pflichtvar).
Hash muss gegen 'hermes-agent' erzeugt werden, der teilt sich das Image
ohne die Pflicht-Var.
2026-07-19 21:41:57 +00:00
ARIA 11341c8521 Dashboard-Auth: Hermes verweigert 0.0.0.0-Bind ohne registrierten Auth-Provider
Hermes' eigenes Auth-Gate blockt den Container-Start hart, wenn --host 0.0.0.0
gesetzt ist aber keine Auth konfiguriert wurde. Basic-Auth-Block in
config.yaml.example ergaenzt, dashboard-Service bekommt HERMES_DASHBOARD_USER
+ HERMES_DASHBOARD_PASSWORD_HASH als Pflicht-Env, .env.example dokumentiert
die Hash-Erzeugung ueber Hermes' eingebautes plugins.dashboard_auth.basic.
2026-07-19 21:37:03 +00:00
ARIA 633cf205ef Fix: hermes-agent-dashboard hatte --host 127.0.0.1 hart im command, ueberschrieb jede Aussen-Config 2026-07-19 21:27:34 +00:00
ARIAandClaude Sonnet 5 e930145e78 Fix: hermes-agent-dashboard scheiterte mit "pull access denied"
Hatte nur image: hermes-agent ohne eigenen build:-Kontext, im Unterschied
zum hermes-agent-Service. Compose versucht bei up erst zu pullen (Image
existiert nicht auf Docker Hub), hermes-agent faengt das per build:-Fallback
ab, dashboard nicht -> harter Error. Fix: gleicher build-Kontext + explizites
pull_policy: build fuer beide, damit gar nicht erst gepullt wird.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 20:29:07 +00:00
ARIA 1c75f79db1 Hermes Agent selbst mit ins Compose: alles auf einer Maschine
Stefan-Entscheidung: Hermes Agent (Nous Research) laeuft auf derselben
Maschine wie der Claude-Max-Proxy, nicht mehr remote. Ergaenzt:

- hermes-agent + hermes-agent-dashboard Services (offizielle Definition,
  gebaut aus separat geklontem hermes-agent-src/), network_mode: host
- hermes-agent-config/config.yaml.example: seedet Hermes' model:-Block
  automatisch auf provider: custom -> unser hermes-gateway, per ${VAR}-
  Expansion aus .env (kein manuelles Config-Editieren mehr)
- hermes-gateway bindet jetzt nur noch auf 127.0.0.1 statt 0.0.0.0, da
  Hermes lokal mitlaeuft und kein Remote-Zugriff mehr noetig ist
- .gitignore fuer hermes-agent-src/, hermes-data/, .env (Secrets/Sourcecode
  gehoeren nicht ins Repo)
- README auf Single-Machine-Architektur aktualisiert, inkl. Setup-Schritten
  und Einordnung des optionalen API_SERVER (fuer spaeteren mobilen Client,
  andere Richtung als unser Proxy)
2026-07-19 20:11:25 +00:00
ARIA ce5fde62f1 Initial Hermes Agent stack: Claude-Max proxy + gateway, docker-compose, README 2026-07-19 19:58:19 +00:00