SLOPSHOPPER

synapse-inbox

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

newstatusnetworktimer
v0.1.0no licenseupdated 2026-10-267blacky7/synapse/mods/synapse-inbox
A shopper browsing a rack in a slop shop
README

Synapse

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.

Lokale KI und Web-Zugänge verwenden gemeinsame Core-Dienste, PostgreSQL und Qdrant.

Zusammenhang und Quellen · Werkzeuge · Schnellstart · Projektstruktur · Konfiguration

Was Synapse bereitstellt

  • Code verstehen: Code-Intelligence erschließt Dateien, Symbole, Referenzen und Abläufe. Parser und Asset-Intelligence behandeln Quelltext und Binärformate mit unterschiedlichen Analyseverfahren.
  • Wissen behalten: Memories, Thoughts und Pläne speichern Erkenntnisse und Aufgaben dauerhaft. Benannte Wissensabrufe kommen aus PostgreSQL, semantische Suchen nutzen den Vektorindex.
  • Änderungen nachvollziehen: Dateiwerkzeuge verbinden Versionierung, Änderungspläne und Co-Edit. Syntaxprüfungen unterstützen den Schreibpfad.
  • Zusammenarbeiten: Agenten, Channels und Events verbinden Koordinator und Spezialisten. Shell und Workspaces binden die Ausführungsumgebung an.

Wie funktioniert Synapse?

Lokal: Claude Code oder Codex

Lokaler stdio-MCP-Zugang mit Daemon und Dateisynchronisation.

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.

Web: claude.ai und Web-UI

Web-KI und Browser erreichen den API-Container über den externen Tunnel.

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.

Agententeams

Koordinator, persistente Kommunikation, Spezialisten-Wrapper und externer Codex-Weg.

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.

Ausführung und Prüfung

Die REST-Shell wählt lokalen Daemon oder Workspace; Syntaxprüfungen haben einen eigenen Container.

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.

Wissen und Embedding

Parser und Chunks bilden die Grundlage für verteilte Ollama-Embeddings und den Qdrant-Index.

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.

Funktionsweise im Detail

Tray · Daemon · Dateiänderungen · Shell · Workspaces

Kommunikation · Wrapper · Jev · Syntaxprüfung

Embedding · Assets · Auth und Projekte · Web-UI und Wissen

Werkzeuge

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.

WerkzeugAufgabeZugang
projectProjektstatus, Einrichtung und projektbezogene Verwaltungstdio / REST-MCP
code_intelDateien, Symbole, Referenzen, Abläufe sowie lexikalische und semantische Codesuchestdio / REST-MCP
asset_intelStruktur und Referenzen von Binärdateien, Medien und Projekt-Assetsstdio / REST-MCP
searchSuche über projektbezogene Wissensbereichestdio / REST-MCP
filesVersionierte Dateiänderungen, Änderungspläne und gemeinsames Bearbeitenstdio / REST-MCP
shellAusführungsaufträge, Status, Logs und Aktivitätstdio / REST-MCP
code_checkSyntaxprüfung, bekannte Fehlermuster und Erklärungsfällestdio / REST-MCP
ignoreProjektbezogene Ausschlussregelnstdio / REST-MCP
memoryBenanntes Projektwissen und semantische Wissenssuchestdio / REST-MCP
thoughtArbeitsnotizen und Berichtestdio / REST-MCP
proposalÄnderungsvorschlägestdio / REST-MCP
planPläne und Aufgabenstdio / REST-MCP
chatAgentenregistrierung und direkte Nachrichtenstdio / REST-MCP
channelGruppenkommunikation und Mitgliedschaftstdio / REST-MCP
eventSteuersignale und Bestätigungenstdio / REST-MCP
specialistPersistente Spezialisten und deren Lebenszyklusstdio / REST-MCP
jevBegrenzte Entscheidungen bei Abwesenheit und Entscheidungsprotokollstdio / REST-MCP
docsTechnische Dokumentation und dateibezogene Hinweisestdio / REST-MCP
adminStatistiken und Wartungsfunktionenstdio / REST-MCP
free_modelsModellpool und Modellabfragenstdio
guideWerkzeugübersicht und Aktionsreferenzstdio / REST-MCP
files_batchSeparater Zugang zu Datei-Batch- und Co-Edit-AktionenREST-MCP
skillsSkill-Inhalte und Abschnitte lesen, suchen und pflegenREST-MCP
workspaceSandboxen, benannte Instanzen und RessourcenREST-MCP
artefaktHTML-Artefakte in den Web-UI-Antwortstrom liefernREST-MCP, Hauptagent
internInterne Diagnose und Worker-SteuerungREST-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.

Schnellstart

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.

Konfiguration

DateiRolle
package.jsonWorkspace-Pakete, Build-/Dev-/Lint-/Clean-Skripte und Node-Anforderung
pnpm-workspace.yamlPaketmenge des pnpm-Workspaces
.mise.tomlNode-Version und Projektaufgaben
docker-compose.ymlAPI-Dienst, optionales bundled-Profil, Daten- und Schema-Volumes
DockerfileBaut Core, API und Web-UI zum REST-API-Image
.mcp.json.exampleBeispielstruktur für lokale MCP-Anbindung und Umgebungsvariablen
shell-setup.example.fishFish-Alias für den Claude-Sitzungswrapper und lokale Konfigurationsquellen
tsconfig.base.jsonGemeinsame 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.

Projektstruktur

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.

Entwicklung und Tests

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.

Source 1 files
hooks/register.ts 83 lines
1import 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