synapsen 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- synapsen-0.1.0/.gitignore +8 -0
- synapsen-0.1.0/CHANGELOG.md +94 -0
- synapsen-0.1.0/LICENSE +157 -0
- synapsen-0.1.0/PKG-INFO +350 -0
- synapsen-0.1.0/README.md +329 -0
- synapsen-0.1.0/docs/ankuendigung.md +161 -0
- synapsen-0.1.0/docs/architektur.md +71 -0
- synapsen-0.1.0/docs/assets/dashboard.png +0 -0
- synapsen-0.1.0/docs/assets/regelkreis.svg +78 -0
- synapsen-0.1.0/docs/assets/verlauf-30-tage.svg +61 -0
- synapsen-0.1.0/docs/profile-schreiben.md +61 -0
- synapsen-0.1.0/examples/dashboard/README.md +22 -0
- synapsen-0.1.0/flake.nix +73 -0
- synapsen-0.1.0/pyproject.toml +54 -0
- synapsen-0.1.0/synapsen/__init__.py +63 -0
- synapsen-0.1.0/synapsen/cli.py +262 -0
- synapsen-0.1.0/synapsen/clock.py +59 -0
- synapsen-0.1.0/synapsen/dynamics.py +240 -0
- synapsen-0.1.0/synapsen/engine.py +652 -0
- synapsen-0.1.0/synapsen/events.py +56 -0
- synapsen-0.1.0/synapsen/explain.py +259 -0
- synapsen-0.1.0/synapsen/journal.py +225 -0
- synapsen-0.1.0/synapsen/mcp_server.py +282 -0
- synapsen-0.1.0/synapsen/profile.py +307 -0
- synapsen-0.1.0/synapsen/profiles.py +151 -0
- synapsen-0.1.0/synapsen/py.typed +0 -0
- synapsen-0.1.0/synapsen/render.py +139 -0
- synapsen-0.1.0/synapsen/simulate.py +299 -0
- synapsen-0.1.0/synapsen/store.py +110 -0
- synapsen-0.1.0/synapsen/validate.py +384 -0
- synapsen-0.1.0/tests/test_behaviour.py +184 -0
- synapsen-0.1.0/tests/test_engine.py +313 -0
- synapsen-0.1.0/tests/test_regressions.py +231 -0
- synapsen-0.1.0/tests/test_tooling.py +340 -0
- synapsen-0.1.0/tools/make_assets.py +242 -0
- synapsen-0.1.0/verify.sh +32 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Änderungsverlauf
|
|
2
|
+
|
|
3
|
+
Format nach [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
|
|
4
|
+
Versionierung nach [SemVer](https://semver.org/lang/de/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — nicht veröffentlicht
|
|
7
|
+
|
|
8
|
+
Erste Fassung. Herausgelöst aus einem Companion-System, das zwei Monate im
|
|
9
|
+
Dauerbetrieb lief; die vier Fehler unten stammen aus diesem Betrieb und wurden
|
|
10
|
+
erst durch die hier neu gebauten Werkzeuge sichtbar.
|
|
11
|
+
|
|
12
|
+
### Neu
|
|
13
|
+
|
|
14
|
+
- `HomeostasisEngine` — Regelkreis aus Botenstoffen, Kopplungen, Tagesrhythmus,
|
|
15
|
+
Ermüdung, Gewöhnung und Bindung
|
|
16
|
+
- `Profile` — die gesamte Biochemie als Daten, inklusive eigener Ereignistabelle
|
|
17
|
+
- `dynamics` — Änderungsraten pro Stunde, Gleichgewichtslöser, Impulsantwort
|
|
18
|
+
- `validate.check()` — Profil-Prüfung, findet pathologische Konfigurationen
|
|
19
|
+
vor dem Betrieb
|
|
20
|
+
- `simulate` — Szenarien, Verläufe, ASCII-Diagramme, CSV-Ausgabe
|
|
21
|
+
- `explain()` — Zerlegung des Zustands in Ereignisse, Rhythmus, Stimmungslage,
|
|
22
|
+
Drift und Kopplung
|
|
23
|
+
- `PromptRenderer` — Zustand als Text, getrennt vom Kern, de/en
|
|
24
|
+
- `JsonStore` mit atomarem Merge-Write; `SqliteJournal`, das sich einem
|
|
25
|
+
vorhandenen Schema anpasst
|
|
26
|
+
- MCP-Server (stdio-JSON-RPC, ohne SDK-Abhängigkeit) mit sieben Werkzeugen
|
|
27
|
+
- Kommandozeile: `doctor`, `show`, `why`, `event`, `simulate`, `events`,
|
|
28
|
+
`profiles`, `mcp`
|
|
29
|
+
- Drei mitgelieferte Profile: `kira`, `focus`, `pad`
|
|
30
|
+
- `flake.nix` für NixOS
|
|
31
|
+
|
|
32
|
+
### Behoben (gegenüber der Ursprungsfassung)
|
|
33
|
+
|
|
34
|
+
- **Kopplungsstärke hing an der Aufruf-Frequenz** statt an der Zeit — Faktor 60
|
|
35
|
+
zwischen minütlichem und stündlichem Tick. Das rechnerische Gleichgewicht für
|
|
36
|
+
Stress lag bei −124, also am Boden; in der echten Zustandsdatei stand
|
|
37
|
+
`cortisol: 0.0` bei gleichzeitig gesättigtem Serotonin und Oxytocin.
|
|
38
|
+
Kopplungen sind jetzt in Einheiten pro Stunde definiert und werden über die
|
|
39
|
+
verstrichene Zeit integriert.
|
|
40
|
+
- **Ein Dauerzustand wurde als Ereignisstrom protokolliert** (99,8 % aller
|
|
41
|
+
Einträge derselbe Typ). Der Stimmungs-Bias summierte diese Einträge und
|
|
42
|
+
schob die Ruhewerte an ihre Anschläge. Jetzt: Entprellung, gewichteter
|
|
43
|
+
Mittelwert statt Summe, begrenzter Ausschlag.
|
|
44
|
+
- **Tagesrhythmus und Stimmungslage wurden nur beim Start berechnet.** Ein
|
|
45
|
+
durchlaufender Dienst blieb im Rhythmus seiner Startstunde stehen. Der
|
|
46
|
+
Ruhewert wird jetzt bei jedem Zeitschritt neu zusammengesetzt.
|
|
47
|
+
- **Ermüdung wuchs unbegrenzt** und drückte den Antrieb nach zwei Tagen
|
|
48
|
+
dauerhaft auf null. Jetzt asymptotisch gesättigt, mit Abbau in den
|
|
49
|
+
Ruhestunden des Profils.
|
|
50
|
+
- **Ein fester Bindungs-Boden hob jeden frischen Agenten sofort auf „sehr
|
|
51
|
+
vertraut".** Der Boden ist jetzt ein Erinnerungswert und greift nur bis zu
|
|
52
|
+
dem Wert, der tatsächlich einmal erreicht wurde.
|
|
53
|
+
- **Singleton beim Import** entfernt: `import` löst keine Datei- oder
|
|
54
|
+
Datenbankzugriffe mehr aus.
|
|
55
|
+
- **Schreibkonflikt** zwischen zwei Prozessen auf derselben Zustandsdatei:
|
|
56
|
+
feldweiser Merge unter Dateisperre, plus ein Besitzkennzeichen, an dem eine
|
|
57
|
+
Engine erkennt, dass ein anderer Schreiber am Zug war.
|
|
58
|
+
|
|
59
|
+
### Behoben (aus der Gegenprüfung dieser Bibliothek)
|
|
60
|
+
|
|
61
|
+
Eine gezielte Prüfrunde gegen die erste grüne Fassung. Alle Punkte sind mit
|
|
62
|
+
Regressionstests belegt, die gegen den Stand von vorher fehlschlagen.
|
|
63
|
+
|
|
64
|
+
- **Der Bindungszerfall war stillgelegt.** Der Erinnerungsboden wurde als
|
|
65
|
+
`min(boden, höchststand)` gerechnet und lag damit rechnerisch immer über dem
|
|
66
|
+
aktuellen Wert — die Bindung konnte weder durch Zeit noch durch `strain_bond()`
|
|
67
|
+
je sinken. Der zugehörige Test bemerkte es nicht, weil er nur „warm > kalt"
|
|
68
|
+
prüfte. Der Boden greift jetzt erst, wenn er tatsächlich erreicht wurde;
|
|
69
|
+
`decay_per_hour` ist neu kalibriert (1.0 → 0.01), weil der Wert nie wirken
|
|
70
|
+
konnte und darum nie stimmen musste.
|
|
71
|
+
- **Unstetigkeit beim Sprung aufs Gleichgewicht.** Bei Lücken über 30 Stunden
|
|
72
|
+
fiel der Ermüdungsdruck weg; eine längere Abwesenheit machte den Agenten
|
|
73
|
+
dadurch wacher.
|
|
74
|
+
- **Die Stimmungslage überlebte den Neustart nicht.** Ohne Protokoll wurde der
|
|
75
|
+
gespeicherte Wert beim Start mit 0.0 überschrieben.
|
|
76
|
+
- **Eine rückwärts laufende Uhr fror den Zustand ein**, bis die Wanduhr
|
|
77
|
+
aufgeholt hatte — und entlud sich dann in einem Satz.
|
|
78
|
+
- **Die Entprellung lag nur im Arbeitsspeicher** und griff darum bei einem
|
|
79
|
+
Prozess je Ereignis (der Kommandozeile) nie.
|
|
80
|
+
- **`resume()` rechnete die Erholung doppelt** an.
|
|
81
|
+
- **Profile ohne Bindung meldeten trotzdem eine Vertrautheit von 50.**
|
|
82
|
+
- **Entartete Profile brachten die Prüfung zum Absturz**, statt gemeldet zu
|
|
83
|
+
werden — beim Werkzeug, dessen Zweck genau das Melden ist.
|
|
84
|
+
- **`explain()` lieferte eine Zerlegung, deren Teile sich nicht zur Summe
|
|
85
|
+
fügten.** Beide Ebenen gehen jetzt exakt auf; was übrig bleibt, steht als
|
|
86
|
+
„Nachlauf" ausdrücklich da.
|
|
87
|
+
- **Das Journal legte für jedes Profil KIRAs fünf Spalten an**; `simulate`
|
|
88
|
+
funktionierte nur mit dem `kira`-Profil; `mcp` verwarf ein mitgeliefertes
|
|
89
|
+
Profil stillschweigend.
|
|
90
|
+
- **`fatigue_targets` hatte konkrete Botenstoffnamen als Vorgabe im Kern** und
|
|
91
|
+
überschrieb beim JSON-Roundtrip ein bewusst leeres Feld.
|
|
92
|
+
- **Ein Ereignis löste einen Schreibvorgang je Botenstoff aus** statt einen.
|
|
93
|
+
- **Vier Tests prüften nichts** (eine Zusicherung war konstant wahr, eine
|
|
94
|
+
verglich einen Wert mit sich selbst) und wurden durch echte ersetzt.
|
synapsen-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Copyright 2026 Thorsten P. (Xarksus)
|
|
2
|
+
|
|
3
|
+
synapsen is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
4
|
+
The full text follows below and is the governing document.
|
|
5
|
+
|
|
6
|
+
In short, for orientation only:
|
|
7
|
+
|
|
8
|
+
Permitted, free of charge — any noncommercial purpose. Personal projects,
|
|
9
|
+
study, research, experiments, hobby work. This includes modifying the
|
|
10
|
+
software and redistributing it, as long as the purpose stays
|
|
11
|
+
noncommercial and these terms travel with the copy. Charities, schools,
|
|
12
|
+
universities, public research bodies, public safety, health and
|
|
13
|
+
environmental organizations and government institutions are covered
|
|
14
|
+
regardless of how they are funded.
|
|
15
|
+
|
|
16
|
+
Requires a separate license — any commercial use. Using it in a product
|
|
17
|
+
or service that generates revenue, selling it, or offering it as a
|
|
18
|
+
hosted or managed service.
|
|
19
|
+
|
|
20
|
+
For a commercial license, contact: thorstenp1980@gmail.com
|
|
21
|
+
|
|
22
|
+
Required Notice: Copyright 2026 Thorsten P. (Xarksus)
|
|
23
|
+
(https://github.com/Xarksus/synapsen)
|
|
24
|
+
|
|
25
|
+
--------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
# PolyForm Noncommercial License 1.0.0
|
|
28
|
+
|
|
29
|
+
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
|
30
|
+
|
|
31
|
+
## Acceptance
|
|
32
|
+
|
|
33
|
+
In order to get any license under these terms, you must agree
|
|
34
|
+
to them as both strict obligations and conditions to all
|
|
35
|
+
your licenses.
|
|
36
|
+
|
|
37
|
+
## Copyright License
|
|
38
|
+
|
|
39
|
+
The licensor grants you a copyright license for the
|
|
40
|
+
software to do everything you might do with the software
|
|
41
|
+
that would otherwise infringe the licensor's copyright
|
|
42
|
+
in it for any permitted purpose. However, you may
|
|
43
|
+
only distribute the software according to [Distribution
|
|
44
|
+
License](#distribution-license) and make changes or new works
|
|
45
|
+
based on the software according to [Changes and New Works
|
|
46
|
+
License](#changes-and-new-works-license).
|
|
47
|
+
|
|
48
|
+
## Distribution License
|
|
49
|
+
|
|
50
|
+
The licensor grants you an additional copyright license
|
|
51
|
+
to distribute copies of the software. Your license
|
|
52
|
+
to distribute covers distributing the software with
|
|
53
|
+
changes and new works permitted by [Changes and New Works
|
|
54
|
+
License](#changes-and-new-works-license).
|
|
55
|
+
|
|
56
|
+
## Notices
|
|
57
|
+
|
|
58
|
+
You must ensure that anyone who gets a copy of any part of
|
|
59
|
+
the software from you also gets a copy of these terms or the
|
|
60
|
+
URL for them above, as well as copies of any plain-text lines
|
|
61
|
+
beginning with `Required Notice:` that the licensor provided
|
|
62
|
+
with the software. For example:
|
|
63
|
+
|
|
64
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
65
|
+
|
|
66
|
+
## Changes and New Works License
|
|
67
|
+
|
|
68
|
+
The licensor grants you an additional copyright license to
|
|
69
|
+
make changes and new works based on the software for any
|
|
70
|
+
permitted purpose.
|
|
71
|
+
|
|
72
|
+
## Patent License
|
|
73
|
+
|
|
74
|
+
The licensor grants you a patent license for the software that
|
|
75
|
+
covers patent claims the licensor can license, or becomes able
|
|
76
|
+
to license, that you would infringe by using the software.
|
|
77
|
+
|
|
78
|
+
## Noncommercial Purposes
|
|
79
|
+
|
|
80
|
+
Any noncommercial purpose is a permitted purpose.
|
|
81
|
+
|
|
82
|
+
## Personal Uses
|
|
83
|
+
|
|
84
|
+
Personal use for research, experiment, and testing for
|
|
85
|
+
the benefit of public knowledge, personal study, private
|
|
86
|
+
entertainment, hobby projects, amateur pursuits, or religious
|
|
87
|
+
observance, without any anticipated commercial application,
|
|
88
|
+
is use for a permitted purpose.
|
|
89
|
+
|
|
90
|
+
## Noncommercial Organizations
|
|
91
|
+
|
|
92
|
+
Use by any charitable organization, educational institution,
|
|
93
|
+
public research organization, public safety or health
|
|
94
|
+
organization, environmental protection organization,
|
|
95
|
+
or government institution is use for a permitted purpose
|
|
96
|
+
regardless of the source of funding or obligations resulting
|
|
97
|
+
from the funding.
|
|
98
|
+
|
|
99
|
+
## Fair Use
|
|
100
|
+
|
|
101
|
+
You may have "fair use" rights for the software under the
|
|
102
|
+
law. These terms do not limit them.
|
|
103
|
+
|
|
104
|
+
## No Other Rights
|
|
105
|
+
|
|
106
|
+
These terms do not allow you to sublicense or transfer any of
|
|
107
|
+
your licenses to anyone else, or prevent the licensor from
|
|
108
|
+
granting licenses to anyone else. These terms do not imply
|
|
109
|
+
any other licenses.
|
|
110
|
+
|
|
111
|
+
## Patent Defense
|
|
112
|
+
|
|
113
|
+
If you make any written claim that the software infringes or
|
|
114
|
+
contributes to infringement of any patent, your patent license
|
|
115
|
+
for the software granted under these terms ends immediately. If
|
|
116
|
+
your company makes such a claim, your patent license ends
|
|
117
|
+
immediately for work on behalf of your company.
|
|
118
|
+
|
|
119
|
+
## Violations
|
|
120
|
+
|
|
121
|
+
The first time you are notified in writing that you have
|
|
122
|
+
violated any of these terms, or done anything with the software
|
|
123
|
+
not covered by your licenses, your licenses can nonetheless
|
|
124
|
+
continue if you come into full compliance with these terms,
|
|
125
|
+
and take practical steps to correct past violations, within
|
|
126
|
+
32 days of receiving notice. Otherwise, all your licenses
|
|
127
|
+
end immediately.
|
|
128
|
+
|
|
129
|
+
## No Liability
|
|
130
|
+
|
|
131
|
+
***As far as the law allows, the software comes as is, without
|
|
132
|
+
any warranty or condition, and the licensor will not be liable
|
|
133
|
+
to you for any damages arising out of these terms or the use
|
|
134
|
+
or nature of the software, under any kind of legal claim.***
|
|
135
|
+
|
|
136
|
+
## Definitions
|
|
137
|
+
|
|
138
|
+
The **licensor** is the individual or entity offering these
|
|
139
|
+
terms, and the **software** is the software the licensor makes
|
|
140
|
+
available under these terms.
|
|
141
|
+
|
|
142
|
+
**You** refers to the individual or entity agreeing to these
|
|
143
|
+
terms.
|
|
144
|
+
|
|
145
|
+
**Your company** is any legal entity, sole proprietorship,
|
|
146
|
+
or other kind of organization that you work for, plus all
|
|
147
|
+
organizations that have control over, are under the control of,
|
|
148
|
+
or are under common control with that organization. **Control**
|
|
149
|
+
means ownership of substantially all the assets of an entity,
|
|
150
|
+
or the power to direct its management and policies by vote,
|
|
151
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
152
|
+
|
|
153
|
+
**Your licenses** are all the licenses granted to you for the
|
|
154
|
+
software under these terms.
|
|
155
|
+
|
|
156
|
+
**Use** means anything you do with the software requiring one
|
|
157
|
+
of your licenses.
|
synapsen-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: synapsen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A homeostatic state core for agents: named neurotransmitters with decay, coupling and circadian rhythm — simulable, verifiable, traceable.
|
|
5
|
+
Project-URL: Homepage, https://github.com/xarksus/synapsen
|
|
6
|
+
Project-URL: Issues, https://github.com/xarksus/synapsen/issues
|
|
7
|
+
Author: Thorsten P.
|
|
8
|
+
License-Expression: PolyForm-Noncommercial-1.0.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: affective-computing,agent,companion,emotion,homeostasis,llm,mcp,persona,state
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# synapsen
|
|
23
|
+
|
|
24
|
+
**A homeostatic state core for agents.** Neurotransmitters instead of mood strings.
|
|
25
|
+
|
|
26
|
+
Most agents have no state — they have an adjective in the prompt.
|
|
27
|
+
`synapsen` models a feedback loop instead: events release neurotransmitters
|
|
28
|
+
that decay, influence each other, and follow a circadian rhythm. Behavior is
|
|
29
|
+
not hardcoded per situation — it is derived from a state that has history.
|
|
30
|
+
|
|
31
|
+
No dependencies beyond the standard library. Python ≥ 3.10.
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install git+https://github.com/Xarksus/synapsen
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from synapsen import HomeostasisEngine, PromptRenderer, JsonStore
|
|
43
|
+
|
|
44
|
+
# Use the bundled kira profile — already calibrated from months of real use
|
|
45
|
+
engine = HomeostasisEngine(store=JsonStore("~/.config/agent/state.json"))
|
|
46
|
+
engine.event("task_success", context="deploy green")
|
|
47
|
+
|
|
48
|
+
# Inject the state into your agent's system prompt
|
|
49
|
+
state_block = PromptRenderer().render(engine.snapshot())
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
[INNER STATE]
|
|
54
|
+
Evening (19:00) | Session 42min | Fatigue 25
|
|
55
|
+
|
|
56
|
+
Neurotransmitters:
|
|
57
|
+
Oxytocin 63.2 | Dopamine 71.4 | Cortisol 11.8 | Serotonin 74.0 | Noradrenaline 38.1
|
|
58
|
+
|
|
59
|
+
States (1.0 = normal, >2.0 = extreme):
|
|
60
|
+
DRIVE 1.06 — Motivation, pace, direct action
|
|
61
|
+
FOCUS 1.36 — Clarity, concentration, precision
|
|
62
|
+
CONNECTED 0.81 — Closeness, openness, trust
|
|
63
|
+
CALM 1.50 — Grounded, no need to prove anything
|
|
64
|
+
TIRED 0.26 — Exhaustion, waning concentration
|
|
65
|
+
…
|
|
66
|
+
|
|
67
|
+
What this means:
|
|
68
|
+
→ CALM (1.50) noticeable: Grounded, no need to prove anything.
|
|
69
|
+
→ Familiarity: 128
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The state block goes into your system prompt. The `What this means` lines are
|
|
73
|
+
state labels rather than imperatives, but a model that reads them will act on
|
|
74
|
+
them — that much is ordinary instruction-following, and this library does not
|
|
75
|
+
claim otherwise. What `synapsen` contributes is not the injection of a line of
|
|
76
|
+
text, but **which line is there and when**: the selection comes from an
|
|
77
|
+
inertial, coupled feedback loop running on real elapsed time, not from an `if`
|
|
78
|
+
on the last event.
|
|
79
|
+
|
|
80
|
+
No sampling parameters are touched. No tool list is modified. Whether this
|
|
81
|
+
produces measurably different behavior than a static line would is an open
|
|
82
|
+
question — see [Limitations](#limitations).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### Example UI Dashboard
|
|
87
|
+
|
|
88
|
+

|
|
89
|
+
|
|
90
|
+
If you prefer a native desktop UI over terminal commands, check out `examples/dashboard/`. It demonstrates how to take the state data (from `synapsen.db` and `hormones.json`) and render it into a beautiful, native GTK3+WebKit2 window with ECharts.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## What's different here
|
|
94
|
+
|
|
95
|
+
Emotion models for agents exist: PAD vectors, appraisal models, mood weights.
|
|
96
|
+
What's missing is a **named, inertial, coupled feedback loop** that runs for
|
|
97
|
+
weeks, can be computed in advance, and whose output can be traced back to its
|
|
98
|
+
causes.
|
|
99
|
+
|
|
100
|
+
| | typical emotion layer | `synapsen` |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| State | vector without meaning | named neurotransmitters with half-life |
|
|
103
|
+
| Time | per call | real elapsed time, frequency-independent |
|
|
104
|
+
| Coupling | none | directed, in units per hour |
|
|
105
|
+
| Rhythm | none | circadian rhythm, fatigue with saturation and decay |
|
|
106
|
+
| History | none | mood of the past week, continuously updated |
|
|
107
|
+
| Bonding | none | grows over weeks, cools with harshness, never fully forgotten |
|
|
108
|
+
| **Prediction** | — | `equilibrium()` computes the resting point |
|
|
109
|
+
| **Validation** | — | `doctor` finds pathological profiles before deployment |
|
|
110
|
+
| **Simulation** | — | months in milliseconds, for tuning and tests |
|
|
111
|
+
| **Traceability** | — | `why` breaks down every value into its causes |
|
|
112
|
+
| Persistence | process lifetime | file or SQLite, cross-process |
|
|
113
|
+
|
|
114
|
+
The default profile is not made-up numbers: these are the values of a system
|
|
115
|
+
that has been running continuously since April 2026 — with 36,649 logged state
|
|
116
|
+
events over the first two months alone, from which the four bugs below came.
|
|
117
|
+
**The calibration work is done. The `kira` profile is ready to use as-is.**
|
|
118
|
+
|
|
119
|
+
## What you don't see until you compute it
|
|
120
|
+
|
|
121
|
+
The original version ran continuously for months without anything obviously
|
|
122
|
+
breaking. These four bugs only became visible through the tools in this package
|
|
123
|
+
— and all four would have been caught by `synapsen doctor` in milliseconds.
|
|
124
|
+
That's exactly why the tools exist.
|
|
125
|
+
|
|
126
|
+
**1 · Coupling strength was tied to call frequency.** The effect was scaled per
|
|
127
|
+
call, not per time. Between "once per minute" and "once per hour" lay a factor
|
|
128
|
+
of 60. The computed equilibrium for stress was −124 — at rock bottom. In the
|
|
129
|
+
actual state file: `cortisol: 0.0`, `serotonin: 140.2` (ceiling 150),
|
|
130
|
+
`oxytocin: 210.7` (ceiling 250). Every value was stuck at a limit; the system
|
|
131
|
+
was saturated and no longer responded to anything.
|
|
132
|
+
|
|
133
|
+
**2 · A persistent state was logged as an event stream.** 99.8% of all entries
|
|
134
|
+
were the same event type. The mood bias *summed* these entries — resting values
|
|
135
|
+
ended up pinned at their rails. Now: debouncing at logging time, weighted
|
|
136
|
+
**average** instead of sum, bounded excursion.
|
|
137
|
+
|
|
138
|
+
**3 · Circadian rhythm and mood were only computed at startup.** A service
|
|
139
|
+
running for weeks stayed in the rhythm of its start hour and the mood of its
|
|
140
|
+
first second. Now the resting value is a sum recomputed fresh at every time step.
|
|
141
|
+
|
|
142
|
+
**4 · Fatigue grew without bound.** Without saturation and without overnight
|
|
143
|
+
decay, fatigue pressure permanently pushed drive to zero after two days.
|
|
144
|
+
The simulation found this on the first run.
|
|
145
|
+
|
|
146
|
+
And because a state model can still be quietly wrong on the second attempt, a
|
|
147
|
+
targeted cross-check was run against this library itself. It found further bugs
|
|
148
|
+
— including a disabled bonding decay (a floor that was computationally always
|
|
149
|
+
above the current value), a discontinuity when jumping to equilibrium, and a
|
|
150
|
+
mood baseline that didn't survive process restart. The history is in the
|
|
151
|
+
[changelog](CHANGELOG.md).
|
|
152
|
+
|
|
153
|
+
Each of these bugs is pinned with a regression test that fails against the
|
|
154
|
+
previous state.
|
|
155
|
+
|
|
156
|
+
## Core concepts
|
|
157
|
+
|
|
158
|
+
**Neurotransmitter** — a value with a resting point, decay rate, and safety ceiling.
|
|
159
|
+
|
|
160
|
+
**Coupling** — directed interaction, in units *per hour*. This unit is why
|
|
161
|
+
equilibrium can be computed: `shift = gain / decay(target)`.
|
|
162
|
+
|
|
163
|
+
**State** — linear combination of neurotransmitters with a name and description.
|
|
164
|
+
All data in the profile, not code.
|
|
165
|
+
|
|
166
|
+
**Event** — what happened, not what should change. `event("task_failure")`
|
|
167
|
+
remains correct when someone swaps the profile; `inject("cortisol", +12)` does not.
|
|
168
|
+
|
|
169
|
+
**Resting value** — not a constant, but
|
|
170
|
+
`baseline + circadian_rhythm + mood + drift`.
|
|
171
|
+
|
|
172
|
+
**Habituation** — frequent releases become blunted.
|
|
173
|
+
|
|
174
|
+
**Bonding** — grows over weeks, cools with harshness, never drops below what
|
|
175
|
+
was once reached.
|
|
176
|
+
|
|
177
|
+
## Tools
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install git+https://github.com/Xarksus/synapsen
|
|
181
|
+
|
|
182
|
+
synapsen profiles # bundled profiles
|
|
183
|
+
synapsen doctor # validate profile — before deployment
|
|
184
|
+
synapsen simulate --days 30 # compute trajectory
|
|
185
|
+
synapsen show # current state as prompt
|
|
186
|
+
synapsen why # trace state back to its causes
|
|
187
|
+
synapsen event task_failure # log an event
|
|
188
|
+
synapsen mcp # run as MCP server
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `doctor` — find bugs before they act for weeks
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
$ synapsen --profile ./my-profile.json doctor
|
|
195
|
+
[ERROR] couplings[0] oxytocin→cortisol: At maximum source, the coupling shifts
|
|
196
|
+
the target by -2880 — more than its entire value range (300).
|
|
197
|
+
→ The target will then be pinned at a limit. Set gain to at most 60.0.
|
|
198
|
+
[ERROR] dynamics: Without any stimulus, cortisol=floor(0) — the system is
|
|
199
|
+
saturated there.
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Checks: resting values against their ceilings, couplings against their target's
|
|
203
|
+
value range, amplifying feedback loops, unknown neurotransmitters in states and
|
|
204
|
+
events, overlapping time windows — and, as an end-to-end test, where the system
|
|
205
|
+
lands without any stimulus.
|
|
206
|
+
|
|
207
|
+
### `simulate` — months in milliseconds
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
$ synapsen simulate --days 14 --event task_failure --keys cortisol,dopamine,CALM
|
|
211
|
+
Work week (task_failure) · Profile kira-v1 · 14 days
|
|
212
|
+
|
|
213
|
+
cortisol ▁▂▂▄█▄▇▆▇▄▇▆▆▅▇▅▄▇▆▆▆▇▄▇▇▆▄▇▅▆▆▆▆▄█▅▆▅▇▄▆▇▆▅▇▅▅▇▆▆▅█▄▇▆▆▄▇▆▆ 12.6 … 31.2
|
|
214
|
+
dopamine █▅▂▃▇▆▂▂▆▇▃▁▅█▄▁▄█▅▂▂▇▆▂▁▆▇▃▁▄█▄▁▃▇▅▂▂▇▆▃▁▅▇▃▁▄█▄▁▃▇▆▂▂▆▇▃▁▅ 15.9 … 54.9
|
|
215
|
+
CALM ▆▇█▇▁▅▄▅▂▄▃▆▃▃▄▆▅▂▄▅▅▂▅▄▅▃▄▄▆▄▃▄▅▆▁▄▅▆▂▅▄▅▃▄▃▆▅▂▄▅▆▁▅▄▅▃▄▃▆▃ 1.2 … 1.6
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
An entire month computed in 0.4 seconds — two good weeks, two hard ones, then
|
|
219
|
+
recovery:
|
|
220
|
+
|
|
221
|
+

|
|
222
|
+
|
|
223
|
+
Clear to see what a pure numbers model can't show: stability breaks later than
|
|
224
|
+
stress rises, and it recovers more slowly than it collapsed. That's the inertia
|
|
225
|
+
this is all about.
|
|
226
|
+
|
|
227
|
+
This lets you tune profiles without waiting weeks — and write behavioral
|
|
228
|
+
regression tests that actually check something meaningful:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
def test_hard_weeks_raise_stress():
|
|
232
|
+
assert max(hard.series("cortisol")) > max(good.series("cortisol")) * 1.3
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `why` — why is the agent like this right now?
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
$ synapsen why
|
|
239
|
+
cortisol 47.3 (rests at 17.5, so +29.8)
|
|
240
|
+
+22.1 events test red 0.8h ago, build red 2.2h ago
|
|
241
|
+
+9.0 circadian morning
|
|
242
|
+
+4.2 mood last 7 days
|
|
243
|
+
-5.5 coupling from oxytocin, serotonin
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Custom profiles
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from synapsen import Profile, HormoneSpec, Coupling, DerivedState, check
|
|
250
|
+
|
|
251
|
+
profile = Profile(
|
|
252
|
+
name="workshop",
|
|
253
|
+
hormones={
|
|
254
|
+
"focus": HormoneSpec(baseline=50, decay=0.4, ceiling=200),
|
|
255
|
+
"restless": HormoneSpec(baseline=15, decay=0.9, ceiling=200),
|
|
256
|
+
},
|
|
257
|
+
couplings=[Coupling("restless", "focus", gain=-4.0, threshold=40)],
|
|
258
|
+
states=[DerivedState("CLEAR", {"focus": 1.0, "restless": -0.5},
|
|
259
|
+
valence="positive", description="able to work")],
|
|
260
|
+
events={"blockade": {"restless": +18, "focus": -8, "severity": -3}},
|
|
261
|
+
)
|
|
262
|
+
assert check(profile).ok
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Bundled are three deliberately very different profiles:
|
|
266
|
+
|
|
267
|
+
- **kira** — five neurotransmitters, bonding, circadian rhythm. Not a starting
|
|
268
|
+
guess: this profile emerged from months of continuous real-world agent
|
|
269
|
+
operation with tens of thousands of logged state events. Use it as-is, or
|
|
270
|
+
as a calibrated baseline for your own profile.
|
|
271
|
+
- **focus** — two axes, no relationship. For a coding agent that gets audibly
|
|
272
|
+
terser after the fifth red build.
|
|
273
|
+
- **pad** — Pleasure–Arousal–Dominance, the academic standard model, expressed
|
|
274
|
+
in this framework.
|
|
275
|
+
|
|
276
|
+
## As an MCP server
|
|
277
|
+
|
|
278
|
+
This lets arbitrary agents — across process and session boundaries — share the
|
|
279
|
+
same state. Pure stdio JSON-RPC, no SDK needed.
|
|
280
|
+
|
|
281
|
+
```jsonc
|
|
282
|
+
{
|
|
283
|
+
"mcpServers": {
|
|
284
|
+
"synapsen": {
|
|
285
|
+
"command": "synapsen-mcp",
|
|
286
|
+
"args": ["--state", "~/.config/agent/state.json"]
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Tools: `state_read`, `state_prompt`, `state_event`, `state_inject`,
|
|
293
|
+
`state_why`, `state_settle`, `state_history`.
|
|
294
|
+
|
|
295
|
+
## NixOS
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
nix run github:Xarksus/synapsen -- doctor
|
|
299
|
+
nix develop # development environment with pytest and ruff
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
The flake provides `packages.default`, `apps.mcp` and a `devShell`.
|
|
303
|
+
|
|
304
|
+
## Migrating from an existing installation
|
|
305
|
+
|
|
306
|
+
`SqliteJournal` adapts to an existing schema rather than replacing it: it reads
|
|
307
|
+
the table's columns and writes what fits. An existing database with
|
|
308
|
+
`emotional_log` works unchanged. For a new journal,
|
|
309
|
+
`SqliteJournal.for_profile(path, profile)` creates the columns of the profile.
|
|
310
|
+
|
|
311
|
+
## Development
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
./verify.sh # tests, linter, all profiles checked — one command
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Or individually:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
pip install -e ".[dev]" # or: nix develop
|
|
321
|
+
pytest -q # 96 tests, ~1 s
|
|
322
|
+
ruff check .
|
|
323
|
+
python tools/make_assets.py # regenerate images
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Limitations
|
|
327
|
+
|
|
328
|
+
**No controlled evaluation yet.** The `kira` profile comes from months of daily
|
|
329
|
+
use, and the behavior looks right. But the only observer was the person who knew
|
|
330
|
+
the state values. A blind A/B test — state block on or off, same prompt, same
|
|
331
|
+
model, independent raters — has not been run. Until it has, treat "it works" as
|
|
332
|
+
a hypothesis, not a result.
|
|
333
|
+
|
|
334
|
+
**The text in the block does the visible work.** The state block contains
|
|
335
|
+
plain-language state labels, and a model that reads them will act on them. The
|
|
336
|
+
claim made here is about *how those lines get selected over time*, not about
|
|
337
|
+
behavior arising from numbers alone.
|
|
338
|
+
|
|
339
|
+
**Neurotransmitter names are a design vocabulary, not biology.** The half-lives
|
|
340
|
+
are tuned for agent behavior, not taken from endocrinology. The names carry
|
|
341
|
+
useful intuitions — habituation, inertia, bonding that cools but never resets —
|
|
342
|
+
and no scientific authority whatsoever.
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
[PolyForm Noncommercial License 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0).
|
|
347
|
+
|
|
348
|
+
Free for any noncommercial purpose — personal projects, study, research, and
|
|
349
|
+
use by nonprofit, educational and government organizations. Commercial use
|
|
350
|
+
requires a separate license: thorstenp1980@gmail.com
|