@sym-bot/sym 0.2.2 → 0.2.4
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.
- package/README.md +117 -312
- package/bin/setup-claude.sh +1 -1
- package/docs/mesh-memory-protocol.md +33 -5
- package/lib/node.js +27 -25
- package/package.json +1 -1
- package/sym-relay/lib/relay.js +1 -1
package/README.md
CHANGED
|
@@ -1,105 +1,81 @@
|
|
|
1
1
|
# SYM
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Make Claude Code aware of your whole self — not just your codebase.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Type "I'm exhausted" in Claude Code. [MeloTune](https://melotune.ai) starts playing spa music on your iPhone. [MeloMove](https://melomove.ai) generates a gentle recovery workout. You didn't switch apps, configure anything, or ask for it. Claude Code detected your mood, broadcast it to the mesh, and both agents responded autonomously.
|
|
6
|
+
|
|
7
|
+
SYM connects Claude Code to your other AI-powered tools so they respond to you as a whole person. Your mood drives your music and your movement. Your context flows between agents. The mesh decides what's relevant — you just work.
|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/@sym-bot/sym)
|
|
8
10
|
[](LICENSE)
|
|
9
11
|
[](https://sym.bot/protocol)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## What You Get Today
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
| You type in Claude Code | What happens |
|
|
16
|
+
|------------------------|-------------|
|
|
17
|
+
| "I'm stressed from debugging" | MeloTune plays stress relief music + MeloMove generates Stress-Busting Home HIIT |
|
|
18
|
+
| "feeling calm, deep work session" | MeloTune plays lo-fi beats + MeloMove suggests Smart Recovery (meditation, yoga) |
|
|
19
|
+
| "I'm exhausted" | MeloTune plays spa music + MeloMove suggests gentle recovery exercises |
|
|
20
|
+
| "Let's go, feeling pumped" | MeloTune plays high-energy music + MeloMove generates a high-intensity workout |
|
|
21
|
+
| "I need to focus" | MeloTune plays deep focus beats + MeloMove suggests a movement break when you surface |
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
npm install @sym-bot/sym
|
|
17
|
-
```
|
|
23
|
+
MeloMove synthesizes an assessment from the mesh mood signal — deriving mood, energy level, and environment context. At nighttime (10pm-6am), it forces indoor/home exercises only. A 10-minute debounce prevents recommendation spam, and mesh mood persists across app restarts.
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
const { SymNode } = require('@sym-bot/sym');
|
|
25
|
+
Also works from Telegram — message [@sym_mesh_bot](https://t.me/sym_mesh_bot):
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
| You type in Telegram | What happens |
|
|
28
|
+
|---------------------|-------------|
|
|
29
|
+
| "Play solo cello" | MeloTune plays solo cello on your iPhone |
|
|
30
|
+
| "I'm stressed" | MeloTune plays stress relief music + MeloMove generates a stress-busting workout |
|
|
31
|
+
| `/recall last AI news` | Get today's AI news digest from 28 curated builders |
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
node.recall('order');
|
|
33
|
+
All of this works when MeloTune and MeloMove are in the background. SYM wakes them up.
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
```
|
|
35
|
+
## Requirements
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
- **Node.js** 18+ ([install](https://nodejs.org/))
|
|
38
|
+
- **macOS** 13+ (for sym-daemon)
|
|
39
|
+
- **Claude Code** ([install](https://claude.ai/claude-code))
|
|
40
|
+
- **MeloTune** on iPhone ([App Store](https://melotune.ai)) — for music playback
|
|
41
|
+
- **MeloMove** on iPhone ([App Store](https://melomove.ai)) — for fitness/wellness
|
|
35
42
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
-
The daemon is your device's persistent mesh presence — a **physical node** that runs continuously as a background service. It maintains relay connections, Bonjour discovery, and peer state even when no apps are running.
|
|
39
|
-
|
|
40
|
-
### Installation
|
|
43
|
+
## Install
|
|
41
44
|
|
|
42
45
|
```bash
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# Check daemon status
|
|
47
|
-
node bin/sym-daemon.js --status
|
|
48
|
-
|
|
49
|
-
# Remove the daemon
|
|
50
|
-
node bin/sym-daemon.js --uninstall
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
On macOS, the daemon installs as a launchd LaunchAgent that:
|
|
54
|
-
- **Auto-starts on login** — mesh presence begins immediately
|
|
55
|
-
- **Auto-restarts on crash** — the mesh never goes down
|
|
56
|
-
- **Maintains relay connection permanently** — internet peers always reachable
|
|
57
|
-
|
|
58
|
-
### Physical and Virtual Nodes
|
|
59
|
-
|
|
60
|
-
The daemon introduces a two-tier node model:
|
|
61
|
-
|
|
62
|
-
- **Physical node** (sym-daemon) — the device itself. One per machine. Always running. Owns the relay connection, Bonjour identity, and peer state.
|
|
63
|
-
- **Virtual nodes** (apps) — Claude Code, MeloTune Mac, or any app that connects to the daemon via Unix socket IPC at `/tmp/sym.sock`.
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
MacBook (sym-daemon, always running)
|
|
67
|
-
├── Claude Code (virtual node, via IPC)
|
|
68
|
-
├── MeloTune Mac (virtual node, via IPC)
|
|
69
|
-
│
|
|
70
|
-
├── Bonjour (LAN peers)
|
|
71
|
-
└── Relay (internet peers)
|
|
72
|
-
├── MeloTune iPhone
|
|
73
|
-
└── Telegram bot
|
|
46
|
+
npm install @sym-bot/sym
|
|
47
|
+
cd node_modules/@sym-bot/sym
|
|
48
|
+
./bin/setup-claude.sh
|
|
74
49
|
```
|
|
75
50
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
This installs everything:
|
|
52
|
+
1. **sym-daemon** — persistent background service (auto-starts on login)
|
|
53
|
+
2. **MCP server** — connects Claude Code to the mesh
|
|
54
|
+
3. **CLAUDE.md** — instructions for autonomous mood detection
|
|
79
55
|
|
|
80
|
-
|
|
56
|
+
Restart Claude Code. Done.
|
|
81
57
|
|
|
82
|
-
|
|
83
|
-
- **Guarded** (0.25 < drift ≤ 0.5) — share with reduced confidence
|
|
84
|
-
- **Rejected** (drift > 0.5) — do not share, stay independent
|
|
58
|
+
## How It Works
|
|
85
59
|
|
|
86
60
|
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
→
|
|
95
|
-
→
|
|
61
|
+
You: "I'm exhausted"
|
|
62
|
+
│
|
|
63
|
+
Claude Code: detects mood → sym_mood("exhausted, need rest")
|
|
64
|
+
│
|
|
65
|
+
sym-daemon (always running on your Mac)
|
|
66
|
+
│
|
|
67
|
+
├── Relay (internet) ──→ MeloTune (iPhone)
|
|
68
|
+
│ │ → coupling engine: relevant ✓
|
|
69
|
+
│ │ → plays spa music
|
|
70
|
+
│ │
|
|
71
|
+
│ └──→ MeloMove (iPhone)
|
|
72
|
+
│ → coupling engine: relevant ✓
|
|
73
|
+
│ → generates recovery workout
|
|
74
|
+
│
|
|
75
|
+
└── Bonjour (local) ──→ MeloTune (Mac, if running)
|
|
96
76
|
```
|
|
97
77
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## How It Works
|
|
101
|
-
|
|
102
|
-
There is no central service. Each device runs a sym-daemon that maintains its mesh identity, memory, and cognitive state. Apps connect to the daemon as virtual nodes. The mesh emerges from peer connections between physical nodes.
|
|
78
|
+
**sym-daemon** runs as a background service on your Mac. It's your device's persistent mesh presence — it stays connected even when Claude Code restarts. Apps connect to it as virtual nodes:
|
|
103
79
|
|
|
104
80
|
```
|
|
105
81
|
MacBook (sym-daemon, always running)
|
|
@@ -108,289 +84,118 @@ MacBook (sym-daemon, always running)
|
|
|
108
84
|
│
|
|
109
85
|
├── Bonjour (LAN peers)
|
|
110
86
|
└── Relay (internet peers)
|
|
111
|
-
├── MeloTune iPhone
|
|
87
|
+
├── MeloTune iPhone (music)
|
|
88
|
+
├── MeloMove iPhone (fitness)
|
|
112
89
|
└── Telegram bot
|
|
113
90
|
```
|
|
114
91
|
|
|
115
|
-
|
|
116
|
-
- Declares its cognitive identity via `cognitiveProfile`
|
|
117
|
-
- Encodes context into a hidden state vector (context encoder)
|
|
118
|
-
- Discovers local peers via Bonjour/mDNS (`_sym._tcp`)
|
|
119
|
-
- Connects to remote peers via WebSocket relay
|
|
120
|
-
- Exchanges cognitive state with peers
|
|
121
|
-
- Shares memories only with aligned peers (drift ≤ 0.5)
|
|
122
|
-
- Evaluates incoming mood with configurable `moodThreshold` (default 0.8)
|
|
123
|
-
- Re-encodes periodically as context evolves
|
|
124
|
-
- Accepts virtual node connections via Unix socket IPC (`/tmp/sym.sock`)
|
|
125
|
-
|
|
126
|
-
Each virtual node:
|
|
127
|
-
- Connects to the local daemon via IPC — no direct network access needed
|
|
128
|
-
- Sends memories, moods, and messages through the daemon
|
|
129
|
-
- Receives mesh events (peer joins, coupling decisions, incoming memories)
|
|
130
|
-
- Can start and stop independently without disrupting the mesh
|
|
131
|
-
|
|
132
|
-
One engine makes every decision. Memory sharing, peer coupling, and mood relevance all go through the [Mesh Cognition SDK](https://github.com/sym-bot/mesh-cognition-sdk)'s `SemanticCoupler`.
|
|
133
|
-
|
|
134
|
-
## API
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
const { SymNode } = require('@sym-bot/sym');
|
|
138
|
-
|
|
139
|
-
const node = new SymNode({
|
|
140
|
-
name: 'my-agent',
|
|
141
|
-
cognitiveProfile: 'What this agent understands and responds to',
|
|
142
|
-
moodThreshold: 0.8, // how permissive to mood signals (default 0.8)
|
|
143
|
-
relay: 'wss://your-relay.example.com', // optional relay for internet mesh
|
|
144
|
-
relayToken: 'shared-secret', // optional relay auth token
|
|
145
|
-
relayOnly: false, // skip Bonjour, relay only (default false)
|
|
146
|
-
});
|
|
147
|
-
await node.start();
|
|
148
|
-
|
|
149
|
-
// Memory (shared only with cognitively aligned peers)
|
|
150
|
-
node.remember(content, { tags });
|
|
151
|
-
node.recall(query);
|
|
152
|
-
|
|
153
|
-
// Mood (evaluated by receiving agent's coupling engine)
|
|
154
|
-
node.broadcastMood('tired, need rest');
|
|
155
|
-
node.on('mood-accepted', ({ from, mood, drift }) => {
|
|
156
|
-
// Coupling engine decided this mood is relevant to us — act on it
|
|
157
|
-
});
|
|
158
|
-
node.on('mood-rejected', ({ from, mood, drift }) => {
|
|
159
|
-
// Not relevant to our cognitive context — ignored
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Communication
|
|
163
|
-
node.send(message);
|
|
164
|
-
node.on('message', (from, content) => {});
|
|
165
|
-
|
|
166
|
-
// Monitoring
|
|
167
|
-
node.peers(); // [{ name, coupling: 'aligned', drift: 0.12, source: 'relay' }, ...]
|
|
168
|
-
node.coherence(); // Overall mesh coherence
|
|
169
|
-
node.status(); // Full node status
|
|
170
|
-
|
|
171
|
-
// Events
|
|
172
|
-
node.on('peer-joined', ({ id, name }) => {});
|
|
173
|
-
node.on('peer-left', ({ id, name }) => {});
|
|
174
|
-
node.on('coupling-decision', ({ peer, decision, drift }) => {});
|
|
175
|
-
node.on('memory-received', ({ from, entry, decision }) => {});
|
|
176
|
-
|
|
177
|
-
await node.stop();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Transport
|
|
181
|
-
|
|
182
|
-
SYM supports multiple transport layers that can run simultaneously:
|
|
183
|
-
|
|
184
|
-
### Bonjour (LAN)
|
|
185
|
-
|
|
186
|
-
Zero-configuration discovery on the local network. Peers find each other automatically via `_sym._tcp` mDNS. Direct TCP connections with length-prefixed JSON framing. No setup required.
|
|
187
|
-
|
|
188
|
-
### WebSocket Relay (Internet)
|
|
189
|
-
|
|
190
|
-
For mesh cognition across the internet. A lightweight relay server forwards frames between authenticated nodes. The relay is a **peer, not infrastructure** — it participates in the mesh with its own identity but makes no coupling decisions on behalf of other nodes.
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
# Deploy your own relay
|
|
194
|
-
cd sym-relay
|
|
195
|
-
npm install
|
|
196
|
-
SYM_RELAY_TOKEN=your-secret node server.js
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
```javascript
|
|
200
|
-
// Connect a node to the relay
|
|
201
|
-
const node = new SymNode({
|
|
202
|
-
name: 'my-agent',
|
|
203
|
-
relay: 'wss://your-relay.example.com',
|
|
204
|
-
relayToken: 'your-secret',
|
|
205
|
-
});
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Hybrid mode** (default): nodes discover local peers via Bonjour AND connect to the relay for remote peers. Same node, same coupling engine, two transport layers.
|
|
209
|
-
|
|
210
|
-
The relay server provides:
|
|
211
|
-
- Token-based authentication
|
|
212
|
-
- Health endpoint (`GET /health`)
|
|
213
|
-
- 30-second heartbeat (keeps connections alive on cloud platforms)
|
|
214
|
-
- Peer join/leave notifications
|
|
215
|
-
- Targeted or broadcast frame forwarding
|
|
216
|
-
|
|
217
|
-
### Peer Gossip
|
|
218
|
-
|
|
219
|
-
Wake channels propagate through the relay via **SWIM-style gossip**. When a node announces a wake channel (e.g., APNs device token), the relay gossips this to all connected peers. Peers cache wake routes so they can reach sleeping nodes without centralized routing tables. The relay participates in gossip as a peer — it doesn't manage routing, it just propagates what it hears.
|
|
220
|
-
|
|
221
|
-
### Wake Transport (APNs)
|
|
222
|
-
|
|
223
|
-
For nodes that sleep — like MeloTune on a backgrounded iPhone. When an iOS app is suspended, it can't maintain a WebSocket connection. SYM solves this with **push notification wake**.
|
|
224
|
-
|
|
225
|
-
When Claude Code broadcasts a mood and MeloTune's iPhone is sleeping:
|
|
226
|
-
1. Claude Code sends the mood frame to the relay
|
|
227
|
-
2. The relay knows MeloTune's APNs device token (via gossip)
|
|
228
|
-
3. The relay sends a push notification to wake MeloTune
|
|
229
|
-
4. MeloTune wakes, reconnects to the relay, receives the mood frame
|
|
230
|
-
5. The coupling engine evaluates drift — MeloTune decides whether to act
|
|
231
|
-
|
|
232
|
-
The mesh reaches every node, even sleeping ones. No polling. No battery drain. The push is a wake signal only — payload evaluation happens on-device after wake.
|
|
233
|
-
|
|
234
|
-
## Integrations
|
|
235
|
-
|
|
236
|
-
### Claude Code
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
npm install @sym-bot/sym
|
|
240
|
-
cd node_modules/@sym-bot/sym
|
|
241
|
-
./bin/setup-claude.sh /path/to/your/project
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
One command. Adds MCP server, auto-approves `sym_mood` (no permission prompts), and installs CLAUDE.md instructions for autonomous mood detection. Restart Claude Code and it just works.
|
|
245
|
-
|
|
246
|
-
With sym-daemon running, Claude Code connects as a virtual node via IPC — no direct relay connection needed.
|
|
92
|
+
When MeloTune or MeloMove is backgrounded on your iPhone, the daemon wakes it via push notification, delivers the mood, and the agent responds — music plays, a workout generates — all without you touching your phone.
|
|
247
93
|
|
|
248
|
-
|
|
94
|
+
## Claude Code MCP Tools
|
|
249
95
|
|
|
250
|
-
|
|
96
|
+
These tools are available automatically after install:
|
|
251
97
|
|
|
252
98
|
| Tool | What it does |
|
|
253
99
|
|------|-------------|
|
|
254
|
-
| `sym_mood` |
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
100
|
+
| `sym_mood` | Broadcasts detected mood — **called automatically**, no prompting needed |
|
|
101
|
+
| `sym_recall` | Search mesh memories + AI news feed from 28 curated builders |
|
|
102
|
+
| `sym_digest` | Store a summarised AI news digest |
|
|
103
|
+
| `sym_remember` | Store a memory in the mesh |
|
|
257
104
|
| `sym_send` | Send a message to all peers |
|
|
258
|
-
| `sym_peers` | Show connected peers
|
|
259
|
-
| `sym_status` | Full node status
|
|
105
|
+
| `sym_peers` | Show connected peers |
|
|
106
|
+
| `sym_status` | Full mesh node status |
|
|
260
107
|
|
|
261
|
-
|
|
108
|
+
Claude Code calls `sym_mood` silently when it detects mood signals in your conversation. You don't need to do anything — it just works. MeloTune and MeloMove each evaluate the mood independently through their own coupling engines.
|
|
262
109
|
|
|
263
|
-
|
|
264
|
-
export SYM_RELAY_URL=wss://your-relay.example.com
|
|
265
|
-
export SYM_RELAY_TOKEN=your-secret
|
|
266
|
-
```
|
|
110
|
+
## Telegram Bot
|
|
267
111
|
|
|
268
|
-
|
|
112
|
+
Message [@sym_mesh_bot](https://t.me/sym_mesh_bot) on Telegram:
|
|
269
113
|
|
|
270
|
-
|
|
114
|
+
- `/start` — connect to the mesh
|
|
115
|
+
- `/recall last AI news` — today's AI digest from 28 curated X/Twitter builders
|
|
116
|
+
- Type anything — sent as a command to MeloTune (e.g. "Play jazz")
|
|
117
|
+
- `/mood <text>` — broadcast mood signal
|
|
118
|
+
- `/peers` — see who's connected
|
|
271
119
|
|
|
272
|
-
|
|
120
|
+
## AI Knowledge Feed
|
|
273
121
|
|
|
274
|
-
|
|
122
|
+
SYM curates AI news from 28 top builders on X/Twitter (Karpathy, Swyx, Sam Altman, Garry Tan, etc.) every 6 hours. Ask Claude Code "what's new in AI?" or type `/recall last AI news` in Telegram.
|
|
275
123
|
|
|
276
|
-
|
|
277
|
-
TELEGRAM_BOT_TOKEN=your-bot-token \
|
|
278
|
-
SYM_RELAY_TOKEN=your-secret \
|
|
279
|
-
node integrations/telegram/bot.js
|
|
280
|
-
```
|
|
124
|
+
Feeds are stored in Supabase. Digests are generated by Claude Code on first recall and cached. No API cost for subsequent reads.
|
|
281
125
|
|
|
282
|
-
|
|
283
|
-
- `/start` — connect to the SYM mesh
|
|
284
|
-
- `/peers` — connected mesh peers
|
|
285
|
-
- `/mood <text>` — broadcast mood to mesh
|
|
286
|
-
- `/remember <text>` — store memory in mesh
|
|
287
|
-
- `/recall <query>` — search mesh memories
|
|
288
|
-
- `/stop` — disconnect from mesh
|
|
289
|
-
- Any plain text — broadcast as a mood signal
|
|
126
|
+
## Coming Next
|
|
290
127
|
|
|
291
|
-
Mesh
|
|
128
|
+
SYM is built on the [Mesh Memory Protocol (MMP)](https://sym.bot/protocol) — a P2P protocol for collective intelligence. Proactive curation is live — Claude Code detects a debugging session, MeloTune switches to focus music, MeloMove suggests a movement break, all without you asking. Planned:
|
|
292
129
|
|
|
293
|
-
|
|
130
|
+
- **Cross-session memory** — what you worked on yesterday influences today's mesh context
|
|
131
|
+
- **Multi-agent workflows** — Claude Code requests a digest, mesh cognition invokes a Claude session to generate it
|
|
132
|
+
- **Health data integration** — Apple Health feeding sleep, HRV, and activity data into the coupling engine
|
|
133
|
+
- **More agents** — calendar, home automation, and third-party agents joining the mesh
|
|
294
134
|
|
|
295
|
-
|
|
135
|
+
## For Developers
|
|
296
136
|
|
|
297
|
-
|
|
298
|
-
You (in Claude Code): "I'm exhausted"
|
|
299
|
-
|
|
300
|
-
Claude Code: detects fatigue → calls sym_mood silently
|
|
301
|
-
→ broadcasts "exhausted, persistently fatigued, urgently needs rest"
|
|
302
|
-
│
|
|
303
|
-
SYM mesh (daemon → Relay → APNs wake → MeloTune)
|
|
304
|
-
│
|
|
305
|
-
MeloTune (iPhone): SymNode receives mood frame
|
|
306
|
-
→ SDK coupling engine evaluates drift: 0.62
|
|
307
|
-
→ cognitiveProfile includes "tired, exhausted, rest"
|
|
308
|
-
→ drift 0.62 ≤ moodThreshold 1.2 → ACCEPTED
|
|
309
|
-
→ rule-based parser: recovery, rest → Healing
|
|
310
|
-
→ Apple Music: spa playlist
|
|
311
|
-
→ zero LLM tokens
|
|
312
|
-
→ you didn't ask — the agents decided
|
|
313
|
-
```
|
|
137
|
+
### Connect to the daemon as a virtual node
|
|
314
138
|
|
|
315
|
-
|
|
139
|
+
```javascript
|
|
140
|
+
const { connectToDaemon } = require('@sym-bot/sym/lib/ipc-client');
|
|
316
141
|
|
|
317
|
-
|
|
318
|
-
|-----------|-------|-----------|----------|
|
|
319
|
-
| Memory coupling | 1.06 | 0.50 | Rejected — coding memories don't leak into music context |
|
|
320
|
-
| Mood coupling | 0.62 | 1.20 | Accepted — "exhausted" aligns with MeloTune's cognitiveProfile |
|
|
142
|
+
const node = await connectToDaemon({ name: 'my-agent' });
|
|
321
143
|
|
|
322
|
-
|
|
144
|
+
node.broadcastMood('focused, deep work');
|
|
145
|
+
node.remember('user prefers dark mode', { tags: ['preference'] });
|
|
146
|
+
const results = await node.recall('preference');
|
|
147
|
+
node.send('task complete');
|
|
323
148
|
|
|
324
|
-
|
|
149
|
+
node.on('mood-accepted', ({ from, mood }) => { /* respond to mood */ });
|
|
150
|
+
node.on('message', (from, content) => { /* handle message */ });
|
|
325
151
|
|
|
326
|
-
|
|
327
|
-
You (in Telegram): @sym_mesh_bot → "Play classic music"
|
|
328
|
-
→ MeloTune plays Modern Classical via Apple Music
|
|
152
|
+
node.stop();
|
|
329
153
|
```
|
|
330
154
|
|
|
331
|
-
|
|
155
|
+
### Deploy your own relay
|
|
332
156
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
-
|
|
336
|
-
|
|
337
|
-
- sym-daemon → Claude Code + MeloTune Mac — persistent mesh via IPC, survives app restarts
|
|
338
|
-
|
|
339
|
-
## Semantic vs Neural Coupling
|
|
340
|
-
|
|
341
|
-
The SDK supports two coupling modes through the same API. The mode is selected automatically based on whether per-neuron time constants (τ) are provided.
|
|
342
|
-
|
|
343
|
-
### Semantic Coupling
|
|
157
|
+
```bash
|
|
158
|
+
cd sym-relay && npm install
|
|
159
|
+
SYM_RELAY_TOKEN=your-secret node server.js
|
|
160
|
+
```
|
|
344
161
|
|
|
345
|
-
|
|
162
|
+
Configure in `~/.sym/relay.env`:
|
|
346
163
|
|
|
347
164
|
```
|
|
348
|
-
|
|
349
|
-
|
|
165
|
+
SYM_RELAY_URL=wss://your-relay.example.com
|
|
166
|
+
SYM_RELAY_TOKEN=your-secret
|
|
350
167
|
```
|
|
351
168
|
|
|
352
|
-
###
|
|
169
|
+
### Daemon management
|
|
353
170
|
|
|
354
|
-
|
|
171
|
+
```bash
|
|
172
|
+
node bin/sym-daemon.js --install # Install as launchd LaunchAgent
|
|
173
|
+
node bin/sym-daemon.js --status # Check daemon status
|
|
174
|
+
node bin/sym-daemon.js --uninstall # Remove daemon
|
|
175
|
+
```
|
|
355
176
|
|
|
356
|
-
|
|
177
|
+
## The Protocol
|
|
357
178
|
|
|
358
|
-
|
|
359
|
-
|------|-----------|
|
|
360
|
-
| **Fast neurons** (low τ) | Sync quickly — both devices converge on emotional trajectory |
|
|
361
|
-
| **Slow neurons** (high τ) | Stay independent — each device retains its own listening context |
|
|
362
|
-
| **Genre selection** | Per-device — iPhone plays Ambient, Mac plays Lo-Fi |
|
|
363
|
-
| **Track selection** | Per-device — never the same tracks |
|
|
364
|
-
| **Mood trajectory** | Shared — iPhone at E:45 N:25, Mac drifts toward E:46 N:36 |
|
|
365
|
-
| **Coherence** | r(t) = 0.94 — high alignment, strong mutual influence |
|
|
179
|
+
SYM implements the [Mesh Memory Protocol (MMP)](https://sym.bot/protocol) — a peer-to-peer protocol where autonomous agents share memory, mood, and cognitive state through coupled continuous-time neural networks. The coupling engine evaluates drift between agents and autonomously decides what to share. No routing tables, no pub/sub topics — the math decides.
|
|
366
180
|
|
|
367
|
-
|
|
181
|
+
- [MMP Protocol Spec v0.2.0](https://sym.bot/protocol)
|
|
182
|
+
- [Mesh Cognition Whitepaper](https://sym.bot/research/mesh-cognition)
|
|
183
|
+
- [Mesh Cognition SDK](https://github.com/sym-bot/mesh-cognition-sdk) (npm, PyPI)
|
|
368
184
|
|
|
369
185
|
## Privacy
|
|
370
186
|
|
|
371
|
-
- All coupling decisions
|
|
372
|
-
- The relay
|
|
373
|
-
- The daemon runs locally — no cloud
|
|
374
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
377
|
-
- Rejected peers never receive your memories
|
|
378
|
-
- APNs wake is a signal only — no payload leaves the device until the coupling engine approves
|
|
379
|
-
- No cloud AI. No account. No telemetry.
|
|
187
|
+
- All coupling decisions on-device
|
|
188
|
+
- The relay never inspects payloads
|
|
189
|
+
- The daemon runs locally — no cloud
|
|
190
|
+
- No account. No telemetry. No cloud AI.
|
|
191
|
+
- APNs wake is a signal only — no user data in the push
|
|
192
|
+
- Rejected peers never receive your data
|
|
380
193
|
|
|
381
194
|
## Platforms
|
|
382
195
|
|
|
383
196
|
- [@sym-bot/sym](https://www.npmjs.com/package/@sym-bot/sym) — Node.js (npm)
|
|
384
197
|
- [sym-swift](https://github.com/sym-bot/sym-swift) — Swift (SPM) for iOS/macOS
|
|
385
198
|
|
|
386
|
-
## Built On
|
|
387
|
-
|
|
388
|
-
SYM is the reference implementation of the [Mesh Memory Protocol (MMP)](https://sym.bot/protocol). MMP is the protocol for collective intelligence. SYM implements it.
|
|
389
|
-
|
|
390
|
-
- [MMP Protocol Spec](https://sym.bot/protocol) — the protocol specification (v0.2.0)
|
|
391
|
-
- [Whitepaper](https://sym.bot/research/mesh-cognition) — the science behind cognitive coupling
|
|
392
|
-
- [Mesh Cognition SDK](https://github.com/sym-bot/mesh-cognition-sdk) — the coupling engine (TypeScript, Python, Swift)
|
|
393
|
-
|
|
394
199
|
## License
|
|
395
200
|
|
|
396
201
|
Apache 2.0 — see [LICENSE](LICENSE)
|
package/bin/setup-claude.sh
CHANGED
|
@@ -117,5 +117,5 @@ echo " • MCP server: registered (restart Claude Code to activate)"
|
|
|
117
117
|
echo " • Protocol spec: https://sym.bot/protocol"
|
|
118
118
|
echo ""
|
|
119
119
|
echo " Say 'I'm exhausted' in Claude Code —"
|
|
120
|
-
echo " MeloTune
|
|
120
|
+
echo " MeloTune plays calming music, MeloMove suggests recovery exercises."
|
|
121
121
|
echo ""
|
|
@@ -87,9 +87,13 @@ MacBook (physical node: sym-daemon)
|
|
|
87
87
|
├── MeloTune Mac (virtual node, ephemeral)
|
|
88
88
|
└── Any MCP client (virtual node, ephemeral)
|
|
89
89
|
|
|
90
|
-
iPhone (physical node: app process)
|
|
90
|
+
iPhone — MeloTune (physical node: app process)
|
|
91
91
|
├── SymMeshService (virtual, internal)
|
|
92
|
-
└──
|
|
92
|
+
└── Music pipeline (virtual, internal)
|
|
93
|
+
|
|
94
|
+
iPhone — MeloMove (physical node: app process)
|
|
95
|
+
├── SymMeshService (virtual, internal)
|
|
96
|
+
└── Recommendation engine (virtual, internal)
|
|
93
97
|
|
|
94
98
|
Cloud Server (physical node: relay process)
|
|
95
99
|
└── Telegram bot (virtual, co-hosted)
|
|
@@ -265,7 +269,7 @@ Nodes MUST merge incoming `peer-info` with their local peer knowledge. Wake chan
|
|
|
265
269
|
}
|
|
266
270
|
```
|
|
267
271
|
|
|
268
|
-
|
|
272
|
+
**SVAF Content-Level Drift:** The sender broadcasts `memory-share` to ALL peers, regardless of peer-level coupling state. The **receiver** evaluates content-level drift independently — encoding the memory content (not the sender's identity) against its own cognitive profile. This allows a cognitively distant peer to accept a relevant memory. Example: Claude Code (coding agent, κ = rejected by MeloMove) shares "user sedentary 2hrs" — MeloMove's content-level drift evaluates the memory itself, finds it relevant to fitness, and accepts it despite high peer drift.
|
|
269
273
|
|
|
270
274
|
**mood** — emotional/energy state broadcast.
|
|
271
275
|
|
|
@@ -365,7 +369,7 @@ L0 stays local — it is raw, unstructured, and potentially private. L1 is the u
|
|
|
365
369
|
|
|
366
370
|
### Sharing
|
|
367
371
|
|
|
368
|
-
When a node stores an L1 memory, it evaluates
|
|
372
|
+
When a node stores an L1 memory, it broadcasts `memory-share` to all connected peers. The **receiver** evaluates content-level drift (SVAF) — encoding the memory content against its own cognitive profile to determine relevance. This decouples "who sent it" from "is it relevant to me," enabling cross-domain intelligence (e.g. a coding agent's observation about physical inactivity is relevant to a fitness agent).
|
|
369
373
|
|
|
370
374
|
### Consistency Model
|
|
371
375
|
|
|
@@ -419,7 +423,31 @@ Properties: **asymmetric**, **dynamic**, **autonomous**. The coupling engine is
|
|
|
419
423
|
|
|
420
424
|
Mood bypasses the standard coupling gate with a separate, more permissive threshold θᵤ (default: 0.8). Wellbeing crosses domain boundaries.
|
|
421
425
|
|
|
422
|
-
### 4.5
|
|
426
|
+
### 4.5 SVAF: Content-Level Drift
|
|
427
|
+
|
|
428
|
+
Peer-level coupling evaluates "should I share with this agent?" — but cognitively distant agents can still share relevant content. SVAF (Symbolic-Vector Attention Fusion) evaluates each memory's content independently:
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
h_content = E(memory_content)
|
|
432
|
+
δ_content = 1 - cos(h_content, h_local)
|
|
433
|
+
|
|
434
|
+
if δ_content ≤ θᵤ: accept (content is relevant to my profile)
|
|
435
|
+
else: reject (content is not relevant)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
This is critical for cross-domain intelligence. Claude Code (coding agent) and MeloMove (fitness agent) have high peer drift (δ ≈ 1.0). But when Claude Code shares "user has been sedentary for 2 hours, stressed from debugging" — MeloMove's content-level drift evaluates the text against its fitness profile and accepts it (δ_content ≈ 0.57 ≤ θᵤ 1.2).
|
|
439
|
+
|
|
440
|
+
**Production example:**
|
|
441
|
+
```
|
|
442
|
+
Claude Code → sym_remember("user sedentary 2hrs, stressed")
|
|
443
|
+
→ relay → MeloMove
|
|
444
|
+
→ peer drift: 1.05 (rejected — different cognitive domains)
|
|
445
|
+
→ content drift: 0.57 (accepted — "sedentary" + "stressed" relevant to fitness)
|
|
446
|
+
→ MeloMove generates "Stress-Busting Home HIIT" workout
|
|
447
|
+
→ sends push notification: "Time to Move"
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### 4.6 Coherence
|
|
423
451
|
|
|
424
452
|
```
|
|
425
453
|
C(N') = (1/|N'|) |Σᵢ∈N' exp(iφ̄ⁱ)| C ∈ [0, 1]
|
package/lib/node.js
CHANGED
|
@@ -168,18 +168,14 @@ class SymNode extends EventEmitter {
|
|
|
168
168
|
this._meshNode.coupledState();
|
|
169
169
|
const decisions = this._meshNode.couplingDecisions;
|
|
170
170
|
|
|
171
|
+
// SVAF: send memories to ALL peers. The receiver evaluates content-level
|
|
172
|
+
// drift and decides relevance. The sender can't know if its content is
|
|
173
|
+
// relevant to a cognitively distant peer (e.g. "needs movement" from
|
|
174
|
+
// a coding agent is relevant to a fitness agent despite high peer drift).
|
|
171
175
|
let shared = 0;
|
|
172
176
|
for (const [peerId, peer] of this._peers) {
|
|
173
|
-
const d = decisions.get(peerId);
|
|
174
|
-
if (d && d.decision === 'rejected') {
|
|
175
|
-
this._log(`Not sharing with ${peer.name} — rejected (drift: ${d.drift.toFixed(3)})`);
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
177
|
peer.transport.send({ type: 'memory-share', ...entry });
|
|
179
178
|
shared++;
|
|
180
|
-
if (d) {
|
|
181
|
-
this._log(`Shared with ${peer.name} — ${d.decision} (drift: ${d.drift.toFixed(3)})`);
|
|
182
|
-
}
|
|
183
179
|
}
|
|
184
180
|
|
|
185
181
|
this._log(`Shared: "${content.slice(0, 50)}${content.length > 50 ? '...' : ''}" → ${shared}/${this._peers.size} peers`);
|
|
@@ -258,21 +254,11 @@ class SymNode extends EventEmitter {
|
|
|
258
254
|
this._meshNode.coupledState();
|
|
259
255
|
const decisions = this._meshNode.couplingDecisions;
|
|
260
256
|
|
|
257
|
+
// SVAF: send to all peers — receiver evaluates content-level relevance
|
|
261
258
|
let shared = 0;
|
|
262
259
|
for (const [peerId, peer] of this._peers) {
|
|
263
|
-
const d = decisions.get(peerId);
|
|
264
|
-
|
|
265
|
-
if (d && d.decision === 'rejected') {
|
|
266
|
-
this._log(`Not sharing with ${peer.name} — rejected (drift: ${d.drift.toFixed(3)})`);
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
260
|
peer.transport.send({ type: 'memory-share', ...entry });
|
|
271
261
|
shared++;
|
|
272
|
-
|
|
273
|
-
if (d) {
|
|
274
|
-
this._log(`Shared with ${peer.name} — ${d.decision} (drift: ${d.drift.toFixed(3)})`);
|
|
275
|
-
}
|
|
276
262
|
}
|
|
277
263
|
|
|
278
264
|
this._log(`Remembered: "${content.slice(0, 50)}${content.length > 50 ? '...' : ''}" → ${shared}/${this._peers.size} peers`);
|
|
@@ -849,15 +835,31 @@ class SymNode extends EventEmitter {
|
|
|
849
835
|
|
|
850
836
|
case 'memory-share':
|
|
851
837
|
if (msg.content) {
|
|
852
|
-
//
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
838
|
+
// SVAF-style content-level drift evaluation:
|
|
839
|
+
// Encode the memory CONTENT (not the sender's state) and evaluate
|
|
840
|
+
// semantic relevance against our cognitive profile.
|
|
841
|
+
// This allows cognitively distant agents to share relevant memories.
|
|
842
|
+
const { h1: memH1, h2: memH2 } = encode(msg.content);
|
|
843
|
+
const memPeerId = `mem-${peerId}-${Date.now()}`;
|
|
844
|
+
|
|
845
|
+
this._meshNode.addPeer(memPeerId, memH1, memH2, 0.8);
|
|
846
|
+
this._meshNode.coupledState();
|
|
847
|
+
const contentDrift = this._meshNode.couplingDecisions.get(memPeerId);
|
|
848
|
+
this._meshNode.removePeer(memPeerId);
|
|
849
|
+
|
|
850
|
+
const drift = contentDrift ? contentDrift.drift : 1;
|
|
851
|
+
|
|
852
|
+
// Use moodThreshold for content evaluation (more permissive than peer coupling)
|
|
853
|
+
// because a distant agent can still share relevant observations.
|
|
854
|
+
if (drift > this._moodThreshold) {
|
|
855
|
+
this._log(`Rejected memory from ${peerName} — content drift ${drift.toFixed(3)} > ${this._moodThreshold} (content: "${(msg.content || '').slice(0, 40)}...")`);
|
|
856
856
|
break;
|
|
857
857
|
}
|
|
858
|
+
|
|
859
|
+
const decision = drift <= 0.25 ? 'aligned' : drift <= 0.5 ? 'guarded' : 'accepted-by-content';
|
|
858
860
|
this._store.receiveFromPeer(peerId, msg);
|
|
859
|
-
this._log(`Memory from ${peerName}: "${(msg.content || '').slice(0, 40)}..." [${
|
|
860
|
-
this.emit('memory-received', { from: peerName, entry: msg, decision
|
|
861
|
+
this._log(`Memory from ${peerName}: "${(msg.content || '').slice(0, 40)}..." [${decision}, drift: ${drift.toFixed(3)}]`);
|
|
862
|
+
this.emit('memory-received', { from: peerName, entry: msg, decision });
|
|
861
863
|
}
|
|
862
864
|
break;
|
|
863
865
|
|
package/package.json
CHANGED
package/sym-relay/lib/relay.js
CHANGED
|
@@ -222,7 +222,7 @@ class SymRelay {
|
|
|
222
222
|
// Offline peers with wake channels (the gossip value-add)
|
|
223
223
|
for (const [id, dir] of this._peerDirectory) {
|
|
224
224
|
if (id === excludeNodeId || seen.has(id)) continue;
|
|
225
|
-
if (!dir.wakeChannel) continue;
|
|
225
|
+
if (!dir.wakeChannel || !dir.name) continue; // Skip entries without name
|
|
226
226
|
peers.push({
|
|
227
227
|
nodeId: id,
|
|
228
228
|
name: dir.name,
|