Synapse Mod-Inbox (P22-T134): Nachrichten an den Ausweis dieser Sitzung starten ueber den lokalen Synapse-Daemon einen Turn

Projektwissen, Code-Analyse und Werkzeuge für lokale und Web-KIs.
Synapse verbindet Projektdateien mit dauerhaftem Wissen und gemeinsamer Agentenarbeit. PostgreSQL hält Inhalte, Aufgaben und Arbeitsstände; Qdrant ergänzt die semantische Suche.
Zusammenhang und Quellen · Werkzeuge · Schnellstart · Projektstruktur · Konfiguration
Ein lokaler MCP-Client spricht den MCP-Server über stdio an; dieser verwendet die gemeinsamen Core-Dienste. Der TypeScript-Daemon verbindet PostgreSQL und Arbeitsverzeichnis und führt lokale Aufträge aus. Der Go-Tray ergänzt die lokale Bedienung.
claude.ai verwendet HTTP-MCP, die Web-UI die REST-Schnittstellen. Die API übernimmt Authentifizierung, Werkzeugaufrufe und die Auslieferung der gebauten Oberfläche; ein vorgeschalteter Cloudflare-Tunnel ermöglicht den externen Zugang. Lokaler HTTP-Zugang ist auch ohne Tunnel möglich; einzelne UI-Bereiche enthalten noch Beispieldaten.
Der Spezialisten-Wrapper liest adressierte Channel-Nachrichten und weckt den verwalteten CLI-Prozess; Channels und Events speichern die Kommunikation. Codex kann über den externen, vom User verwalteten cc-wrap arbeiten, mit cc-send als kurzem Wecksignal. Jev kann bei aktivierter Abwesenheitsregel begrenzte Rückfragen entscheiden.
REST-Shell-Aufträge gehen automatisch an einen erreichbaren lokalen Daemon oder andernfalls an einen Workspace; das Ziel lässt sich ausdrücklich wählen. Der Prüfcontainer ist separat und prüft Syntax ohne Netzwerkzugang. Podman-Workspaces ergänzen Container-Werkzeuge innerhalb der Sandbox.
Die Code-Pipeline erzeugt Symbole und Chunks; Server-Worker oder Compute-Nodes bearbeiten Embedding-Claims mit Ollama. Die API prüft zurückgelieferte Vektoren vor der Übernahme in Qdrant. Modellprofile bestimmen Modell und Dimension; das dokumentierte Nemotron-3-Embed-1B-Profil verwendet 1024 Dimensionen, keinen festverdrahteten Modell-Fallback.
Die Zusammenhangsdokumentation erklärt die Schnittstellen genauer. Die Bildübersicht verknüpft jede Ansicht mit ihren Codequellen; Konzepte sind separat ausgewiesen.
Tray · Daemon · Dateiänderungen · Shell · Workspaces
Kommunikation · Wrapper · Jev · Syntaxprüfung
Embedding · Assets · Auth und Projekte · Web-UI und Wissen
Die Tabelle dient zur Orientierung. Die genaue Aktionsreferenz liefert guide; maßgeblich für Verfügbarkeit und Parameter sind das stdio-Register und das REST-MCP-Register. Einzelne Aktionen und kontextabhängige Werkzeuge können sich unterscheiden.
| Werkzeug | Aufgabe | Zugang |
|---|---|---|
| project | Projektstatus, Einrichtung und projektbezogene Verwaltung | stdio / REST-MCP |
| code_intel | Dateien, Symbole, Referenzen, Abläufe sowie lexikalische und semantische Codesuche | stdio / REST-MCP |
| asset_intel | Struktur und Referenzen von Binärdateien, Medien und Projekt-Assets | stdio / REST-MCP |
| search | Suche über projektbezogene Wissensbereiche | stdio / REST-MCP |
| files | Versionierte Dateiänderungen, Änderungspläne und gemeinsames Bearbeiten | stdio / REST-MCP |
| shell | Ausführungsaufträge, Status, Logs und Aktivität | stdio / REST-MCP |
| code_check | Syntaxprüfung, bekannte Fehlermuster und Erklärungsfälle | stdio / REST-MCP |
| ignore | Projektbezogene Ausschlussregeln | stdio / REST-MCP |
| memory | Benanntes Projektwissen und semantische Wissenssuche | stdio / REST-MCP |
| thought | Arbeitsnotizen und Berichte | stdio / REST-MCP |
| proposal | Änderungsvorschläge | stdio / REST-MCP |
| plan | Pläne und Aufgaben | stdio / REST-MCP |
| chat | Agentenregistrierung und direkte Nachrichten | stdio / REST-MCP |
| channel | Gruppenkommunikation und Mitgliedschaft | stdio / REST-MCP |
| event | Steuersignale und Bestätigungen | stdio / REST-MCP |
| specialist | Persistente Spezialisten und deren Lebenszyklus | stdio / REST-MCP |
| jev | Begrenzte Entscheidungen bei Abwesenheit und Entscheidungsprotokoll | stdio / REST-MCP |
| docs | Technische Dokumentation und dateibezogene Hinweise | stdio / REST-MCP |
| admin | Statistiken und Wartungsfunktionen | stdio / REST-MCP |
| free_models | Modellpool und Modellabfragen | stdio |
| guide | Werkzeugübersicht und Aktionsreferenz | stdio / REST-MCP |
| files_batch | Separater Zugang zu Datei-Batch- und Co-Edit-Aktionen | REST-MCP |
| skills | Skill-Inhalte und Abschnitte lesen, suchen und pflegen | REST-MCP |
| workspace | Sandboxen, benannte Instanzen und Ressourcen | REST-MCP |
| artefakt | HTML-Artefakte in den Web-UI-Antwortstrom liefern | REST-MCP, Hauptagent |
| intern | Interne Diagnose und Worker-Steuerung | REST-MCP, explizit aktiviert |
Die Parser liefern formatabhängige statische Analysen; ein Index ersetzt weder Compiler noch Laufzeittest. Die Testübersichten dokumentieren die vorhandenen Prüfsuiten.
Vorausgesetzt sind mise, pnpm sowie erreichbare PostgreSQL-, Qdrant- und konfigurierte Embedding-Dienste. .mise.toml legt Node 20 und die Projektaufgaben fest.
cd ~/dev/synapse
mise install
pnpm install --frozen-lockfile
mise run build
Die Verbindungseinstellungen kommen aus der Umgebung beziehungsweise einer lokalen .env; Beispiele für den MCP-Client stehen in .mcp.json.example. Datenbank- und Embedding-Konfiguration sind vor dem Start erforderlich. mise run dev startet den lokalen MCP-Entwicklungsserver, mise run dev:api die REST-API.
Für Container gibt es Dockerfile und Compose; dessen optionales bundled-Profil ergänzt PostgreSQL und Qdrant. Das Laufzeitschema ist vom SQL-Kaltstartspiegel zu unterscheiden; Schema-Modus und Startverhalten sind im Datenbankbereich eingeordnet.
| Datei | Rolle |
|---|---|
| package.json | Workspace-Pakete, Build-/Dev-/Lint-/Clean-Skripte und Node-Anforderung |
| pnpm-workspace.yaml | Paketmenge des pnpm-Workspaces |
| .mise.toml | Node-Version und Projektaufgaben |
| docker-compose.yml | API-Dienst, optionales bundled-Profil, Daten- und Schema-Volumes |
| Dockerfile | Baut Core, API und Web-UI zum REST-API-Image |
| .mcp.json.example | Beispielstruktur für lokale MCP-Anbindung und Umgebungsvariablen |
| shell-setup.example.fish | Fish-Alias für den Claude-Sitzungswrapper und lokale Konfigurationsquellen |
| tsconfig.base.json | Gemeinsame TypeScript-Compileroptionen |
Die Core-Konfiguration und die Embedding-Module beschreiben Datenbank-, Such- und Provider-Anbindungen. Modellwerte stammen aus dem konfigurierten Profil. API-/Daemon- und Container-Einstellungen stehen in den jeweiligen Ordner-READMEs.
Jeder Link führt zur README des betreffenden Ordners. Die aufklappbaren Bäume enthalten auch interne Module und Testdaten; Einstiegspunkte sind Pakete, Container und Dokumentation.
Builds und Entwicklung verwenden die Aufgaben aus .mise.toml. Tests sind in Core, REST-API, MCP-Server, Daemon und Skripten beschrieben.
Datenbanktests verwenden ausschließlich synapse_test auf Port 5433 mit eigenen Testdaten. Projektbezogene Agentenregeln stehen in AGENTS.md; ergänzende Anleitungen und Arbeitsregeln sind unter Skills eingeordnet.
hooks/register.ts 83 lines1import type { Register } from 'claude-code'
2
3// P22-T134 Mod-Inbox: wartet am lokalen Synapse-Daemon (mod-inbox.ts) auf Nachrichten an den Ausweis dieser Sitzung und
4// startet damit einen Turn. Den Ausweis kennt nur der Daemon (aus dem Transcript); die Mod meldet nur ihre Sitzungs-ID.
5// P22-T151 (Doppelzustellung inbox:1100): $.prompt.submit wartet, bis die Sitzung untaetig ist. Die Abrufschleife wartet deshalb
6// nicht mehr darauf (sonst gilt die Sitzung im Daemon nach 60 s ohne Abruf als beendet und die Nachricht kommt erneut), meldet
7// 'angenommen' sofort und reiht in eine eigene Kette ein (submits nacheinander, in Reihenfolge). Dieselbe Nachricht (id) geht nie
8// zweimal an $.prompt.submit, auch wenn der Daemon sie nach einem Neustart erneut uebergibt.
9const PAUSE_BEI_FEHLER_MS = 10_000
10/** So viele Nachrichten-IDs merkt sich die Mod fuer die Dedup (aelteste fallen heraus). */
11const GEMERKT_MAX = 500
12
13interface ModNachricht { id: string; text: string }
14interface WarteAntwort { nachricht?: ModNachricht | null; aktiv?: boolean; grund?: string | null }
15
16export const register: Register = on => {
17 on('session.start', async ($, e, next) => {
18 const sitzung = await $.session.id()
19 const home = (await $.env.get('HOME')) ?? (await $.env.get('USERPROFILE')) ?? ''
20 const portDatei = `${home}/.synapse/file-watcher/daemon.port`
21 let aktivAngezeigt: boolean | null = null
22 // id -> 'wartet' (eingereiht bzw. submit laeuft) | 'gestartet'
23 const gemerkt = new Map<string, 'wartet' | 'gestartet'>()
24 let kette: Promise<void> = Promise.resolve()
25
26 const merke = (id: string, stand: 'wartet' | 'gestartet'): void => {
27 gemerkt.delete(id)
28 gemerkt.set(id, stand)
29 while (gemerkt.size > GEMERKT_MAX) gemerkt.delete(gemerkt.keys().next().value as string)
30 }
31
32 // Meldung an den Daemon; ein Fehler oder 404 (Daemon vor P22-T151 kennt /angenommen nicht) hat keine Folgen fuer die Schleife.
33 const melde = async (basis: string, pfad: 'angenommen' | 'quittung', body: Record<string, unknown>): Promise<void> => {
34 try {
35 await $.http.fetch(`${basis}/${pfad}`, {
36 method: 'POST',
37 headers: { 'content-type': 'application/json' },
38 body: JSON.stringify({ sitzung, ...body }),
39 })
40 } catch { /* der naechste Job derselben Nachricht fragt erneut, die Antwort kommt dann aus gemerkt */ }
41 }
42
43 const nimmAn = (basis: string, n: ModNachricht): void => {
44 const stand = gemerkt.get(n.id)
45 if (stand === 'gestartet') { void melde(basis, 'quittung', { id: n.id, gestartet: true, grund: null }); return }
46 void melde(basis, 'angenommen', { id: n.id })
47 if (stand === 'wartet') return
48 merke(n.id, 'wartet')
49 kette = kette.then(async () => {
50 // submit wartet, bis die Sitzung untaetig ist, und loest mit dem Turnstart auf. P22-T142: asUser ohne Claude-Code-Rahmen;
51 // die Herkunft nennt der Kopf aus API/Daemon ('[Synapse-Nachricht] ... Von:'), Hooks sehen weiter kind 'plugin'.
52 let drop: string | undefined
53 try { drop = (await $.prompt.submit({ text: n.text, asUser: true })).drop } catch (err) { drop = String(err).slice(0, 200) }
54 // drop: vergessen, damit ein erneuter Versuch der API wieder einen submit ausloest.
55 if (drop === undefined) merke(n.id, 'gestartet')
56 else gemerkt.delete(n.id)
57 await melde(basis, 'quittung', { id: n.id, gestartet: drop === undefined, grund: drop ?? null })
58 })
59 }
60
61 // Laeuft ueber das Ende des Hooks hinaus: ein Abruf haelt am Daemon bis zu 25 s, danach sofort der naechste.
62 const runde = async (): Promise<void> => {
63 let pause = 1
64 try {
65 const basis = `http://127.0.0.1:${(await $.fs.read(portDatei)).trim()}/mod-inbox`
66 const r = await $.http.fetch(`${basis}/warte?sitzung=${sitzung}`)
67 if (!r.ok) throw new Error(`HTTP ${r.status}`)
68 const j = JSON.parse(r.text) as WarteAntwort
69 if ((j.aktiv === true) !== aktivAngezeigt) {
70 aktivAngezeigt = j.aktiv === true
71 $.ui.status(aktivAngezeigt ? 'Synapse-Inbox aktiv' : undefined)
72 }
73 if (j.nachricht) nimmAn(basis, j.nachricht)
74 } catch {
75 pause = PAUSE_BEI_FEHLER_MS
76 }
77 $.clock.after(pause, () => { void runde() })
78 }
79 void runde()
80 return next(e)
81 })
82}
83