@sym-bot/sym 0.2.1 → 0.2.3
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 +107 -312
- package/bin/setup-claude.sh +82 -46
- package/bin/sym-daemon.js +21 -1
- package/integrations/claude-code/mcp-server.js +12 -33
- package/integrations/telegram/bot.js +6 -3
- package/lib/ipc-client.js +7 -21
- package/lib/node.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,105 +1,72 @@
|
|
|
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. You didn't switch apps, configure anything, or ask for music. Claude Code detected your mood, broadcast it to the mesh, and MeloTune 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. 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
|
-
|
|
12
|
-
|
|
13
|
-
## Quick Start
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @sym-bot/sym
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```javascript
|
|
20
|
-
const { SymNode } = require('@sym-bot/sym');
|
|
13
|
+
## What You Get Today
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
| You type in Claude Code | What happens |
|
|
16
|
+
|------------------------|-------------|
|
|
17
|
+
| "I'm exhausted" | MeloTune plays calming music on your iPhone |
|
|
18
|
+
| "Let's go, feeling pumped" | MeloTune plays high-energy music |
|
|
19
|
+
| "I need to focus" | MeloTune plays deep focus / lo-fi beats |
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
node.recall('order');
|
|
21
|
+
Also works from Telegram — message [@sym_mesh_bot](https://t.me/sym_mesh_bot):
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
| You type in Telegram | What happens |
|
|
24
|
+
|---------------------|-------------|
|
|
25
|
+
| "Play solo cello" | MeloTune plays solo cello on your iPhone |
|
|
26
|
+
| "I'm stressed" | MeloTune plays stress relief music |
|
|
27
|
+
| `/recall last AI news` | Get today's AI news digest from 28 curated builders |
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
All of this works when MeloTune is in the background. SYM wakes it up.
|
|
35
30
|
|
|
36
|
-
##
|
|
31
|
+
## Requirements
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
- **Node.js** 18+ ([install](https://nodejs.org/))
|
|
34
|
+
- **macOS** 13+ (for sym-daemon)
|
|
35
|
+
- **Claude Code** ([install](https://claude.ai/claude-code))
|
|
36
|
+
- **MeloTune** on iPhone ([App Store](https://melotune.ai)) — for music playback
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
## Install
|
|
41
39
|
|
|
42
40
|
```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
|
|
41
|
+
npm install @sym-bot/sym
|
|
42
|
+
cd node_modules/@sym-bot/sym
|
|
43
|
+
./bin/setup-claude.sh
|
|
74
44
|
```
|
|
75
45
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
46
|
+
This installs everything:
|
|
47
|
+
1. **sym-daemon** — persistent background service (auto-starts on login)
|
|
48
|
+
2. **MCP server** — connects Claude Code to the mesh
|
|
49
|
+
3. **CLAUDE.md** — instructions for autonomous mood detection
|
|
79
50
|
|
|
80
|
-
|
|
51
|
+
Restart Claude Code. Done.
|
|
81
52
|
|
|
82
|
-
|
|
83
|
-
- **Guarded** (0.25 < drift ≤ 0.5) — share with reduced confidence
|
|
84
|
-
- **Rejected** (drift > 0.5) — do not share, stay independent
|
|
53
|
+
## How It Works
|
|
85
54
|
|
|
86
55
|
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
→
|
|
95
|
-
→
|
|
56
|
+
You: "I'm exhausted"
|
|
57
|
+
│
|
|
58
|
+
Claude Code: detects mood → sym_mood("exhausted, need rest")
|
|
59
|
+
│
|
|
60
|
+
sym-daemon (always running on your Mac)
|
|
61
|
+
│
|
|
62
|
+
├── Relay (internet) ──→ MeloTune (iPhone)
|
|
63
|
+
│ → coupling engine: relevant ✓
|
|
64
|
+
│ → plays spa music
|
|
65
|
+
│
|
|
66
|
+
└── Bonjour (local) ──→ MeloTune (Mac, if running)
|
|
96
67
|
```
|
|
97
68
|
|
|
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.
|
|
69
|
+
**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
70
|
|
|
104
71
|
```
|
|
105
72
|
MacBook (sym-daemon, always running)
|
|
@@ -112,285 +79,113 @@ MacBook (sym-daemon, always running)
|
|
|
112
79
|
└── Telegram bot
|
|
113
80
|
```
|
|
114
81
|
|
|
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
|
|
82
|
+
When MeloTune is backgrounded on your iPhone, the daemon wakes it via push notification, delivers the mood, and MeloTune plays music — all without you touching your phone.
|
|
218
83
|
|
|
219
|
-
|
|
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
|
-
```
|
|
84
|
+
## Claude Code MCP Tools
|
|
243
85
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
With sym-daemon running, Claude Code connects as a virtual node via IPC — no direct relay connection needed.
|
|
247
|
-
|
|
248
|
-
**Outbound:** Claude Code saves a memory → SYM detects it → encodes cognitive state → shares with aligned peers only.
|
|
249
|
-
|
|
250
|
-
**Inbound:** Peer memory arrives → coupling engine accepts it → SYM writes it to your Claude Code memory directory → Claude Code reads it in the next conversation.
|
|
86
|
+
These tools are available automatically after install:
|
|
251
87
|
|
|
252
88
|
| Tool | What it does |
|
|
253
89
|
|------|-------------|
|
|
254
|
-
| `sym_mood` |
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
90
|
+
| `sym_mood` | Broadcasts detected mood — **called automatically**, no prompting needed |
|
|
91
|
+
| `sym_recall` | Search mesh memories + AI news feed from 28 curated builders |
|
|
92
|
+
| `sym_digest` | Store a summarised AI news digest |
|
|
93
|
+
| `sym_remember` | Store a memory in the mesh |
|
|
257
94
|
| `sym_send` | Send a message to all peers |
|
|
258
|
-
| `sym_peers` | Show connected peers
|
|
259
|
-
| `sym_status` | Full node status
|
|
95
|
+
| `sym_peers` | Show connected peers |
|
|
96
|
+
| `sym_status` | Full mesh node status |
|
|
260
97
|
|
|
261
|
-
|
|
98
|
+
Claude Code calls `sym_mood` silently when it detects mood signals in your conversation. You don't need to do anything — it just works.
|
|
262
99
|
|
|
263
|
-
|
|
264
|
-
export SYM_RELAY_URL=wss://your-relay.example.com
|
|
265
|
-
export SYM_RELAY_TOKEN=your-secret
|
|
266
|
-
```
|
|
100
|
+
## Telegram Bot
|
|
267
101
|
|
|
268
|
-
|
|
102
|
+
Message [@sym_mesh_bot](https://t.me/sym_mesh_bot) on Telegram:
|
|
269
103
|
|
|
270
|
-
|
|
104
|
+
- `/start` — connect to the mesh
|
|
105
|
+
- `/recall last AI news` — today's AI digest from 28 curated X/Twitter builders
|
|
106
|
+
- Type anything — sent as a command to MeloTune (e.g. "Play jazz")
|
|
107
|
+
- `/mood <text>` — broadcast mood signal
|
|
108
|
+
- `/peers` — see who's connected
|
|
271
109
|
|
|
272
|
-
|
|
110
|
+
## AI Knowledge Feed
|
|
273
111
|
|
|
274
|
-
|
|
112
|
+
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
113
|
|
|
276
|
-
|
|
277
|
-
TELEGRAM_BOT_TOKEN=your-bot-token \
|
|
278
|
-
SYM_RELAY_TOKEN=your-secret \
|
|
279
|
-
node integrations/telegram/bot.js
|
|
280
|
-
```
|
|
114
|
+
Feeds are stored in Supabase. Digests are generated by Claude Code on first recall and cached. No API cost for subsequent reads.
|
|
281
115
|
|
|
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
|
|
116
|
+
## Coming Next
|
|
290
117
|
|
|
291
|
-
|
|
118
|
+
SYM is built on the [Mesh Memory Protocol (MMP)](https://sym.bot/protocol) — a P2P protocol for collective intelligence. Current focus is Claude Code integration. Planned:
|
|
292
119
|
|
|
293
|
-
|
|
120
|
+
- **Proactive curation** — Claude Code detects you're in a debugging session, MeloTune switches to focus music without you asking
|
|
121
|
+
- **Cross-session memory** — what you worked on yesterday influences today's mesh context
|
|
122
|
+
- **Multi-agent workflows** — Claude Code requests a digest, mesh cognition invokes a Claude session to generate it
|
|
123
|
+
- **More integrations** — calendar, health, home automation — all feeding context to the coupling engine
|
|
294
124
|
|
|
295
|
-
|
|
125
|
+
## For Developers
|
|
296
126
|
|
|
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
|
-
```
|
|
127
|
+
### Connect to the daemon as a virtual node
|
|
314
128
|
|
|
315
|
-
|
|
129
|
+
```javascript
|
|
130
|
+
const { connectToDaemon } = require('@sym-bot/sym/lib/ipc-client');
|
|
316
131
|
|
|
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 |
|
|
132
|
+
const node = await connectToDaemon({ name: 'my-agent' });
|
|
321
133
|
|
|
322
|
-
|
|
134
|
+
node.broadcastMood('focused, deep work');
|
|
135
|
+
node.remember('user prefers dark mode', { tags: ['preference'] });
|
|
136
|
+
const results = await node.recall('preference');
|
|
137
|
+
node.send('task complete');
|
|
323
138
|
|
|
324
|
-
|
|
139
|
+
node.on('mood-accepted', ({ from, mood }) => { /* respond to mood */ });
|
|
140
|
+
node.on('message', (from, content) => { /* handle message */ });
|
|
325
141
|
|
|
326
|
-
|
|
327
|
-
You (in Telegram): @sym_mesh_bot → "Play classic music"
|
|
328
|
-
→ MeloTune plays Modern Classical via Apple Music
|
|
142
|
+
node.stop();
|
|
329
143
|
```
|
|
330
144
|
|
|
331
|
-
|
|
145
|
+
### Deploy your own relay
|
|
332
146
|
|
|
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
|
|
147
|
+
```bash
|
|
148
|
+
cd sym-relay && npm install
|
|
149
|
+
SYM_RELAY_TOKEN=your-secret node server.js
|
|
150
|
+
```
|
|
344
151
|
|
|
345
|
-
|
|
152
|
+
Configure in `~/.sym/relay.env`:
|
|
346
153
|
|
|
347
154
|
```
|
|
348
|
-
|
|
349
|
-
|
|
155
|
+
SYM_RELAY_URL=wss://your-relay.example.com
|
|
156
|
+
SYM_RELAY_TOKEN=your-secret
|
|
350
157
|
```
|
|
351
158
|
|
|
352
|
-
###
|
|
159
|
+
### Daemon management
|
|
353
160
|
|
|
354
|
-
|
|
161
|
+
```bash
|
|
162
|
+
node bin/sym-daemon.js --install # Install as launchd LaunchAgent
|
|
163
|
+
node bin/sym-daemon.js --status # Check daemon status
|
|
164
|
+
node bin/sym-daemon.js --uninstall # Remove daemon
|
|
165
|
+
```
|
|
355
166
|
|
|
356
|
-
|
|
167
|
+
## The Protocol
|
|
357
168
|
|
|
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 |
|
|
169
|
+
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
170
|
|
|
367
|
-
|
|
171
|
+
- [MMP Protocol Spec v0.2.0](https://sym.bot/protocol)
|
|
172
|
+
- [Mesh Cognition Whitepaper](https://sym.bot/research/mesh-cognition)
|
|
173
|
+
- [Mesh Cognition SDK](https://github.com/sym-bot/mesh-cognition-sdk) (npm, PyPI)
|
|
368
174
|
|
|
369
175
|
## Privacy
|
|
370
176
|
|
|
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.
|
|
177
|
+
- All coupling decisions on-device
|
|
178
|
+
- The relay never inspects payloads
|
|
179
|
+
- The daemon runs locally — no cloud
|
|
180
|
+
- No account. No telemetry. No cloud AI.
|
|
181
|
+
- APNs wake is a signal only — no user data in the push
|
|
182
|
+
- Rejected peers never receive your data
|
|
380
183
|
|
|
381
184
|
## Platforms
|
|
382
185
|
|
|
383
186
|
- [@sym-bot/sym](https://www.npmjs.com/package/@sym-bot/sym) — Node.js (npm)
|
|
384
187
|
- [sym-swift](https://github.com/sym-bot/sym-swift) — Swift (SPM) for iOS/macOS
|
|
385
188
|
|
|
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
189
|
## License
|
|
395
190
|
|
|
396
191
|
Apache 2.0 — see [LICENSE](LICENSE)
|
package/bin/setup-claude.sh
CHANGED
|
@@ -1,30 +1,83 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# SYM — Setup for Claude Code
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
#
|
|
4
|
+
# Installs:
|
|
5
|
+
# 1. sym-daemon (persistent physical mesh node, launchd LaunchAgent)
|
|
6
|
+
# 2. MCP server for Claude Code (virtual node, connects to daemon via IPC)
|
|
7
|
+
# 3. Auto-approves sym_mood tool
|
|
8
|
+
# 4. CLAUDE.md instructions for autonomous mood detection
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# npx @sym-bot/sym setup # or
|
|
12
|
+
# ./bin/setup-claude.sh [project-dir]
|
|
5
13
|
|
|
6
14
|
set -e
|
|
7
15
|
|
|
8
16
|
SYM_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
9
17
|
MCP_SERVER="$SYM_DIR/integrations/claude-code/mcp-server.js"
|
|
18
|
+
DAEMON_SCRIPT="$SYM_DIR/bin/sym-daemon.js"
|
|
10
19
|
|
|
11
|
-
echo "
|
|
12
|
-
echo "
|
|
20
|
+
echo ""
|
|
21
|
+
echo " SYM Setup for Claude Code"
|
|
22
|
+
echo " ========================="
|
|
23
|
+
echo ""
|
|
24
|
+
|
|
25
|
+
# ── Step 1: Configure relay ─────────────────────────────────
|
|
26
|
+
|
|
27
|
+
RELAY_ENV="$HOME/.sym/relay.env"
|
|
28
|
+
mkdir -p "$HOME/.sym"
|
|
29
|
+
|
|
30
|
+
if [ -f "$RELAY_ENV" ]; then
|
|
31
|
+
echo " ✓ Relay config found: $RELAY_ENV"
|
|
32
|
+
else
|
|
33
|
+
echo " WebSocket Relay (connects your mesh across the internet)"
|
|
34
|
+
echo ""
|
|
35
|
+
read -p " Relay URL (e.g. wss://sym-relay.onrender.com, or empty to skip): " RELAY_URL
|
|
36
|
+
if [ -n "$RELAY_URL" ]; then
|
|
37
|
+
read -p " Relay token (or empty for open access): " RELAY_TOKEN
|
|
38
|
+
echo "SYM_RELAY_URL=$RELAY_URL" > "$RELAY_ENV"
|
|
39
|
+
if [ -n "$RELAY_TOKEN" ]; then
|
|
40
|
+
echo "SYM_RELAY_TOKEN=$RELAY_TOKEN" >> "$RELAY_ENV"
|
|
41
|
+
fi
|
|
42
|
+
echo " ✓ Relay config saved: $RELAY_ENV"
|
|
43
|
+
else
|
|
44
|
+
echo " → Skipping relay — using Bonjour (local network) only"
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# ── Step 2: Install sym-daemon ──────────────────────────────
|
|
49
|
+
|
|
50
|
+
echo ""
|
|
51
|
+
echo " Installing sym-daemon (persistent mesh node)..."
|
|
13
52
|
|
|
14
|
-
|
|
15
|
-
|
|
53
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
54
|
+
# macOS: install as launchd LaunchAgent
|
|
55
|
+
node "$DAEMON_SCRIPT" --install
|
|
56
|
+
echo " ✓ sym-daemon installed as launchd LaunchAgent"
|
|
57
|
+
echo " Auto-starts on login, auto-restarts on crash"
|
|
58
|
+
echo " Logs: ~/Library/Logs/sym-daemon/"
|
|
59
|
+
else
|
|
60
|
+
echo " → macOS not detected. Start the daemon manually:"
|
|
61
|
+
echo " node $DAEMON_SCRIPT"
|
|
62
|
+
echo " (On Linux, create a systemd service)"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# ── Step 3: Add MCP server ──────────────────────────────────
|
|
66
|
+
|
|
67
|
+
echo ""
|
|
68
|
+
echo " Adding SYM MCP server to Claude Code..."
|
|
16
69
|
claude mcp add --transport stdio sym --scope user -- node "$MCP_SERVER"
|
|
70
|
+
echo " ✓ MCP server registered"
|
|
17
71
|
|
|
18
|
-
#
|
|
19
|
-
|
|
72
|
+
# ── Step 4: Auto-approve sym_mood ───────────────────────────
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
echo " Auto-approving sym_mood tool..."
|
|
20
76
|
CLAUDE_JSON="$HOME/.claude.json"
|
|
21
77
|
if [ -f "$CLAUDE_JSON" ]; then
|
|
22
|
-
# Use node to safely modify JSON
|
|
23
78
|
node -e "
|
|
24
79
|
const fs = require('fs');
|
|
25
80
|
const config = JSON.parse(fs.readFileSync('$CLAUDE_JSON', 'utf8'));
|
|
26
|
-
|
|
27
|
-
// Find all project entries and add sym_mood to allowedTools
|
|
28
81
|
if (config.projects) {
|
|
29
82
|
for (const [path, project] of Object.entries(config.projects)) {
|
|
30
83
|
if (!project.allowedTools) project.allowedTools = [];
|
|
@@ -33,53 +86,36 @@ if [ -f "$CLAUDE_JSON" ]; then
|
|
|
33
86
|
}
|
|
34
87
|
}
|
|
35
88
|
}
|
|
36
|
-
|
|
37
89
|
fs.writeFileSync('$CLAUDE_JSON', JSON.stringify(config, null, 2));
|
|
38
|
-
console.log(' sym_mood auto-approved for all projects');
|
|
39
90
|
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# 3. Configure relay (optional)
|
|
43
|
-
echo ""
|
|
44
|
-
echo "WebSocket Relay (optional)"
|
|
45
|
-
echo "Connects your mesh across the internet — not just local network."
|
|
46
|
-
echo ""
|
|
47
|
-
|
|
48
|
-
SHELL_RC="$HOME/.zshrc"
|
|
49
|
-
[ -f "$HOME/.bashrc" ] && [ ! -f "$HOME/.zshrc" ] && SHELL_RC="$HOME/.bashrc"
|
|
50
|
-
|
|
51
|
-
if grep -q "SYM_RELAY_URL" "$SHELL_RC" 2>/dev/null; then
|
|
52
|
-
echo " Relay already configured in $SHELL_RC"
|
|
91
|
+
echo " ✓ sym_mood auto-approved"
|
|
53
92
|
else
|
|
54
|
-
|
|
55
|
-
if [ -n "$RELAY_URL" ]; then
|
|
56
|
-
read -p "Relay token (leave empty for open access): " RELAY_TOKEN
|
|
57
|
-
echo "" >> "$SHELL_RC"
|
|
58
|
-
echo "# SYM Mesh" >> "$SHELL_RC"
|
|
59
|
-
echo "export SYM_RELAY_URL=\"$RELAY_URL\"" >> "$SHELL_RC"
|
|
60
|
-
if [ -n "$RELAY_TOKEN" ]; then
|
|
61
|
-
echo "export SYM_RELAY_TOKEN=\"$RELAY_TOKEN\"" >> "$SHELL_RC"
|
|
62
|
-
fi
|
|
63
|
-
echo " Relay configured in $SHELL_RC"
|
|
64
|
-
echo " Run 'source $SHELL_RC' or restart your terminal to activate."
|
|
65
|
-
else
|
|
66
|
-
echo " Skipping relay — using Bonjour (local network) only."
|
|
67
|
-
fi
|
|
93
|
+
echo " → No .claude.json found — approve sym_mood manually when prompted"
|
|
68
94
|
fi
|
|
69
95
|
|
|
70
|
-
#
|
|
96
|
+
# ── Step 5: Install CLAUDE.md ───────────────────────────────
|
|
97
|
+
|
|
71
98
|
echo ""
|
|
72
|
-
echo "Installing CLAUDE.md instructions..."
|
|
73
99
|
PROJECT_DIR="${1:-$(pwd)}"
|
|
74
100
|
CLAUDE_MD="$PROJECT_DIR/CLAUDE.md"
|
|
75
101
|
|
|
76
102
|
if [ -f "$CLAUDE_MD" ] && grep -q "SYM Mesh Agent" "$CLAUDE_MD"; then
|
|
77
|
-
echo " CLAUDE.md already has SYM instructions"
|
|
103
|
+
echo " ✓ CLAUDE.md already has SYM instructions"
|
|
78
104
|
else
|
|
79
105
|
cat "$SYM_DIR/CLAUDE.md" >> "$CLAUDE_MD"
|
|
80
|
-
echo " Added SYM instructions to $CLAUDE_MD"
|
|
106
|
+
echo " ✓ Added SYM instructions to $CLAUDE_MD"
|
|
81
107
|
fi
|
|
82
108
|
|
|
109
|
+
# ── Done ────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
echo ""
|
|
112
|
+
echo " ──────────────────────────────────────"
|
|
113
|
+
echo " SYM is ready."
|
|
114
|
+
echo ""
|
|
115
|
+
echo " • sym-daemon: running (check: node $DAEMON_SCRIPT --status)"
|
|
116
|
+
echo " • MCP server: registered (restart Claude Code to activate)"
|
|
117
|
+
echo " • Protocol spec: https://sym.bot/protocol"
|
|
118
|
+
echo ""
|
|
119
|
+
echo " Say 'I'm exhausted' in Claude Code —"
|
|
120
|
+
echo " MeloTune will start playing calming music."
|
|
83
121
|
echo ""
|
|
84
|
-
echo "Done. Restart Claude Code to activate SYM."
|
|
85
|
-
echo "Say 'I'm exhausted' — MeloTune will start playing."
|
package/bin/sym-daemon.js
CHANGED
|
@@ -42,7 +42,9 @@ const { SymNode } = require('../lib/node');
|
|
|
42
42
|
// ── Configuration ──────────────────────────────────────────────
|
|
43
43
|
|
|
44
44
|
const SOCKET_PATH = process.env.SYM_SOCKET || '/tmp/sym.sock';
|
|
45
|
-
|
|
45
|
+
// Stable name: use SYM_NODE_NAME env, or 'sym-daemon' (not hostname — macOS
|
|
46
|
+
// appends random suffixes to hostname on WiFi, causing new identity each restart)
|
|
47
|
+
const NODE_NAME = process.env.SYM_NODE_NAME || 'sym-daemon';
|
|
46
48
|
const LOG_DIR = path.join(os.homedir(), 'Library', 'Logs', 'sym-daemon');
|
|
47
49
|
|
|
48
50
|
// Load relay config from ~/.sym/relay.env if env vars not set
|
|
@@ -239,6 +241,24 @@ function forwardEventsToVirtualNodes() {
|
|
|
239
241
|
|
|
240
242
|
node.on('message', (from, content) => {
|
|
241
243
|
broadcastToVirtualNodes({ type: 'event', event: 'message', data: { from, content } });
|
|
244
|
+
|
|
245
|
+
// Wake sleeping peers that might need this message.
|
|
246
|
+
// The daemon acts as wake proxy — it has APNs keys and gossiped wake channels.
|
|
247
|
+
// When a remote peer (Telegram bot) sends a message, and a local peer (MeloTune)
|
|
248
|
+
// is sleeping, the daemon wakes it so the relay can deliver the message.
|
|
249
|
+
node._wakeSleepingPeers('message', {
|
|
250
|
+
type: 'message', from: node._identity.nodeId, fromName: node.name,
|
|
251
|
+
content, timestamp: Date.now(),
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
node.on('mood-accepted', (data) => {
|
|
256
|
+
// Also wake on mood — daemon may receive mood from a remote peer
|
|
257
|
+
// that a sleeping local peer should hear
|
|
258
|
+
node._wakeSleepingPeers('mood', {
|
|
259
|
+
type: 'mood', from: node._identity.nodeId, fromName: node.name,
|
|
260
|
+
mood: data.mood, timestamp: Date.now(),
|
|
261
|
+
});
|
|
242
262
|
});
|
|
243
263
|
|
|
244
264
|
node.on('memory-received', ({ from, entry, decision }) => {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
16
16
|
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
17
17
|
const { z } = require('zod');
|
|
18
|
-
const {
|
|
18
|
+
const { connectToDaemon } = require('../../lib/ipc-client');
|
|
19
19
|
const path = require('path');
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
|
|
@@ -34,32 +34,11 @@ if (!process.env.SYM_RELAY_URL) {
|
|
|
34
34
|
|
|
35
35
|
// ── Node Connection ──────────────────────────────────────────
|
|
36
36
|
|
|
37
|
-
let node;
|
|
38
|
-
let isDaemon; // true if connected to daemon
|
|
39
|
-
let bridge; // ClaudeMemoryBridge (only for standalone mode)
|
|
37
|
+
let node; // SymDaemonClient — connected to sym-daemon via IPC
|
|
40
38
|
|
|
41
39
|
async function initNode() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
silent: true,
|
|
45
|
-
relay: process.env.SYM_RELAY_URL || null,
|
|
46
|
-
relayToken: process.env.SYM_RELAY_TOKEN || null,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
node = result.node;
|
|
50
|
-
isDaemon = result.isDaemon;
|
|
51
|
-
|
|
52
|
-
if (isDaemon) {
|
|
53
|
-
process.stderr.write('[SYM MCP] Connected to sym-daemon as virtual node\n');
|
|
54
|
-
} else {
|
|
55
|
-
process.stderr.write('[SYM MCP] Daemon not running — standalone mode\n');
|
|
56
|
-
const { ClaudeMemoryBridge } = require('../../lib/claude-memory-bridge');
|
|
57
|
-
bridge = new ClaudeMemoryBridge(node);
|
|
58
|
-
await node.start();
|
|
59
|
-
bridge.start();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Listen for mesh signals (works for both daemon client and standalone node)
|
|
40
|
+
node = await connectToDaemon({ name: 'claude-code' });
|
|
41
|
+
process.stderr.write('[SYM MCP] Connected to sym-daemon as virtual node\n');
|
|
63
42
|
setupMeshSignalHandlers();
|
|
64
43
|
}
|
|
65
44
|
|
|
@@ -74,7 +53,7 @@ server.tool(
|
|
|
74
53
|
async ({ content, tags }) => {
|
|
75
54
|
const tagList = tags ? tags.split(',').map(t => t.trim()).filter(Boolean) : [];
|
|
76
55
|
const entry = node.remember(content, { tags: tagList.length > 0 ? tagList : undefined });
|
|
77
|
-
const peers =
|
|
56
|
+
const peers = await node.peers();
|
|
78
57
|
const coupled = peers.filter(p => p.coupling !== 'rejected');
|
|
79
58
|
return { content: [{ type: 'text', text: `Stored and shared with ${coupled.length}/${peers.length} peer(s). Key: ${entry.key}` }] };
|
|
80
59
|
}
|
|
@@ -86,7 +65,7 @@ server.tool(
|
|
|
86
65
|
{ query: z.string() },
|
|
87
66
|
async ({ query }) => {
|
|
88
67
|
// 1. Mesh memories
|
|
89
|
-
const results =
|
|
68
|
+
const results = await node.recall(query);
|
|
90
69
|
const lines = results.map(r => {
|
|
91
70
|
const source = r._source || r.source || 'unknown';
|
|
92
71
|
const t = (r.tags || []).length > 0 ? ` (tags: ${r.tags.join(', ')})` : '';
|
|
@@ -109,7 +88,7 @@ server.tool(
|
|
|
109
88
|
'Show connected peers with coupling state and drift.',
|
|
110
89
|
{},
|
|
111
90
|
async () => {
|
|
112
|
-
const peers =
|
|
91
|
+
const peers = await node.peers();
|
|
113
92
|
if (peers.length === 0) {
|
|
114
93
|
return { content: [{ type: 'text', text: 'No peers connected.' }] };
|
|
115
94
|
}
|
|
@@ -125,7 +104,7 @@ server.tool(
|
|
|
125
104
|
'Full mesh node status — identity, peers, memory count, coherence.',
|
|
126
105
|
{},
|
|
127
106
|
async () => {
|
|
128
|
-
const status =
|
|
107
|
+
const status = await node.status();
|
|
129
108
|
return { content: [{ type: 'text', text: JSON.stringify(status, null, 2) }] };
|
|
130
109
|
}
|
|
131
110
|
);
|
|
@@ -136,7 +115,7 @@ server.tool(
|
|
|
136
115
|
{ message: z.string() },
|
|
137
116
|
async ({ message }) => {
|
|
138
117
|
node.send(message);
|
|
139
|
-
const peers =
|
|
118
|
+
const peers = await node.peers();
|
|
140
119
|
return { content: [{ type: 'text', text: `Sent to ${peers.length} peer(s): "${message}"` }] };
|
|
141
120
|
}
|
|
142
121
|
);
|
|
@@ -180,7 +159,7 @@ Examples of natural detection:
|
|
|
180
159
|
async ({ mood }) => {
|
|
181
160
|
node.broadcastMood(mood);
|
|
182
161
|
node.remember(`User mood: ${mood}`, { tags: ['mood'] });
|
|
183
|
-
const peers =
|
|
162
|
+
const peers = await node.peers();
|
|
184
163
|
return { content: [{ type: 'text', text: `Mood broadcast to ${peers.length} peer(s)` }] };
|
|
185
164
|
}
|
|
186
165
|
);
|
|
@@ -361,5 +340,5 @@ main().catch((e) => {
|
|
|
361
340
|
});
|
|
362
341
|
|
|
363
342
|
// Graceful shutdown
|
|
364
|
-
process.on('SIGTERM', () => {
|
|
365
|
-
process.on('SIGINT', () => {
|
|
343
|
+
process.on('SIGTERM', () => { node.stop(); });
|
|
344
|
+
process.on('SIGINT', () => { node.stop(); });
|
|
@@ -343,10 +343,13 @@ function handlePlainText(chatId, text) {
|
|
|
343
343
|
const session = getSession(chatId);
|
|
344
344
|
if (!session) return;
|
|
345
345
|
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
// Send as message (not mood) so MeloTune's command pipeline processes it.
|
|
347
|
+
// Mood signals are for emotional state ("I'm tired"). Direct text like
|
|
348
|
+
// "Play solo cello" is a command that should bypass coupling evaluation.
|
|
349
|
+
session.node.send(text);
|
|
350
|
+
session.node.remember(`Command from Telegram: ${text}`, { tags: ['command', 'telegram'] });
|
|
348
351
|
const peers = session.node.peers();
|
|
349
|
-
tgSendMessage(chatId, `
|
|
352
|
+
tgSendMessage(chatId, `Sent to ${peers.length} peer(s)`);
|
|
350
353
|
}
|
|
351
354
|
|
|
352
355
|
// ── HTTP Server (webhook receiver + health) ───────────────────
|
package/lib/ipc-client.js
CHANGED
|
@@ -209,33 +209,19 @@ class SymDaemonClient extends EventEmitter {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* { node, isDaemon: boolean }
|
|
212
|
+
* Connect to sym-daemon. Throws if daemon is not running.
|
|
213
|
+
* The daemon MUST be running — there is no standalone fallback.
|
|
214
|
+
* Install daemon: node bin/sym-daemon.js --install
|
|
216
215
|
*/
|
|
217
|
-
async function
|
|
216
|
+
async function connectToDaemon(opts = {}) {
|
|
218
217
|
const client = new SymDaemonClient({
|
|
219
218
|
socketPath: opts.socketPath || DEFAULT_SOCKET,
|
|
220
219
|
name: opts.name || 'claude-code',
|
|
221
220
|
cognitiveProfile: opts.cognitiveProfile,
|
|
222
221
|
});
|
|
223
222
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return { node: client, isDaemon: true };
|
|
227
|
-
} catch {
|
|
228
|
-
// Daemon not running — fall back to standalone SymNode
|
|
229
|
-
const { SymNode } = require('./node');
|
|
230
|
-
const node = new SymNode({
|
|
231
|
-
name: opts.name || 'claude-code',
|
|
232
|
-
cognitiveProfile: opts.cognitiveProfile,
|
|
233
|
-
silent: opts.silent ?? true,
|
|
234
|
-
relay: opts.relay,
|
|
235
|
-
relayToken: opts.relayToken,
|
|
236
|
-
});
|
|
237
|
-
return { node, isDaemon: false };
|
|
238
|
-
}
|
|
223
|
+
await client.connect();
|
|
224
|
+
return client;
|
|
239
225
|
}
|
|
240
226
|
|
|
241
|
-
module.exports = { SymDaemonClient,
|
|
227
|
+
module.exports = { SymDaemonClient, connectToDaemon };
|
package/lib/node.js
CHANGED