overmind-mcp 3.5.2 → 3.6.1
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/dist/bridge/BridgeProxy.d.ts +4 -2
- package/dist/bridge/BridgeProxy.d.ts.map +1 -1
- package/dist/bridge/BridgeProxy.js +31 -13
- package/dist/bridge/BridgeProxy.js.map +1 -1
- package/dist/bridge/types.d.ts +1 -1
- package/dist/bridge/types.d.ts.map +1 -1
- package/dist/bridge/types.js +4 -2
- package/dist/bridge/types.js.map +1 -1
- package/docs/overmind-bridge-guide.md +665 -0
- package/package.json +1 -1
- package/docs/SOUL_pdf_bon_travail_v2.md +0 -132
- package/docs/agent-http-tutorial.md +0 -524
- package/docs/doc_guide_agent_hermes_permanent.md +0 -315
- package/docs/guide_agent_hermes_overmind.md +0 -403
- package/docs/overmind-bridge-persistent-agents.md +0 -892
|
@@ -1,892 +0,0 @@
|
|
|
1
|
-
# 🌉 Overmind Bridge — Agents Persistants & A2A
|
|
2
|
-
|
|
3
|
-
> **Version**: Overmind v3.5.0 — `src/bridge/` (1928 lignes OverBridgeServer + 443 OverBridgeService + 259 AgentRegistry)
|
|
4
|
-
> **Date**: 2026-07-09
|
|
5
|
-
> **Source**: `src/bridge/OverBridgeServer.ts`, `src/bridge/OverBridgeService.ts`, `src/bridge/AgentRegistry.ts`, `src/bin/overmind-bridge.ts`, `discord_llm/src/overmind-bridge.ts`
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Table des Matières
|
|
10
|
-
|
|
11
|
-
1. [Vue d'Ensemble](#vue-densemble)
|
|
12
|
-
2. [Architecture du Bridge](#architecture-du-bridge)
|
|
13
|
-
3. [Les 3 Couches de Persistance](#les-3-couches-de-persistance)
|
|
14
|
-
4. [AgentRegistry — État Live en Mémoire](#agentregistry--état-live-en-mémoire)
|
|
15
|
-
5. [SessionStore — Sessions Multi-Tenant](#sessionstore--sessions-multi-tenant)
|
|
16
|
-
6. [MessageLog — Persistence Postgres](#messagelog--persistence-postgres)
|
|
17
|
-
7. [JSON-RPC 2.0 — Toutes les Méthodes](#json-rpc-20--toutes-les-méthodes)
|
|
18
|
-
8. [A2A — Agent-to-Agent](#a2a--agent-to-agent)
|
|
19
|
-
9. [A2A Extended — Broadcast, Pipeline, Fanout, Delegate, Query](#a2a-extended--broadcast-pipeline-fanout-delegate-query)
|
|
20
|
-
10. [Comment Construire un Agent Persistant (comme discord_llm)](#comment-construire-un-agent-persistant-comme-discord_llm)
|
|
21
|
-
11. [CLI overmind-bridge](#cli-overmind-bridge)
|
|
22
|
-
12. [Démarrer le Serveur Bridge](#démarrer-le-serveur-bridge)
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Vue d'Ensemble
|
|
27
|
-
|
|
28
|
-
L'Overmind Bridge est un **serveur HTTP JSON-RPC 2.0** qui transforme les appels MCP one-shot en conversations persistantes et multi-tenant. C'est la couche qui rend les agents "always-on" — même entre deux requêtes Discord, l'état de l'agent (session, contexte, mémoire) est préservé.
|
|
29
|
-
|
|
30
|
-
### Le Problème Résolu
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
Sans Bridge:
|
|
34
|
-
Discord !sniper → spawn hermes CLI → répond → meurt
|
|
35
|
-
Discord !sniper → spawn hermes CLI → répond → meurt (perte de contexte!)
|
|
36
|
-
|
|
37
|
-
Avec Bridge:
|
|
38
|
-
Discord !sniper → POST /rpc agent.run → agent (session persistée)
|
|
39
|
-
Discord !sniper → POST /rpc agent.run → agent (MÊME session, contexte conservé!)
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Architecture du Bridge
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
┌──────────────────────────────────────────────────────────────────┐
|
|
48
|
-
│ CLIENTS (Discord, curl, Python) │
|
|
49
|
-
│ │
|
|
50
|
-
│ discord_llm/overmind-bridge.ts overmind-bridge CLI │
|
|
51
|
-
│ (Express, port 3001) (call, scenario, status) │
|
|
52
|
-
└──────────────────────┬───────────────────────┬───────────────────┘
|
|
53
|
-
│ POST /rpc │
|
|
54
|
-
│ JSON-RPC 2.0 │
|
|
55
|
-
▼ ▼
|
|
56
|
-
┌──────────────────────────────────────────────────────────────────┐
|
|
57
|
-
│ OverBridgeServer (port 3100) │
|
|
58
|
-
│ src/bridge/OverBridgeServer.ts │
|
|
59
|
-
│ │
|
|
60
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
61
|
-
│ │ dispatchRpc() — route les méthodes JSON-RPC │ │
|
|
62
|
-
│ │ agent.run | agent.a2a | agent.broadcast | agent.pipeline │ │
|
|
63
|
-
│ │ agent.fanout | agent.delegate | agent.query │ │
|
|
64
|
-
│ │ agent.status | agent.list | agent.kill │ │
|
|
65
|
-
│ │ message.history | message.get | message.replay | stats │ │
|
|
66
|
-
│ │ session.get | session.list | session.delete | session.stats│ │
|
|
67
|
-
│ │ webhook.sms | health.ping │ │
|
|
68
|
-
│ └─────────────────────────────────────────────────────────────┘ │
|
|
69
|
-
│ │
|
|
70
|
-
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
|
|
71
|
-
│ │ AgentRegistry│ │ SessionStore │ │ MessageLog (Postgres) │ │
|
|
72
|
-
│ │ (in-memory) │ │ (JSON file) │ │ (bridge_messages table) │ │
|
|
73
|
-
│ │ busy/idle │ │ externalKey │ │ from/to/prompt/response │ │
|
|
74
|
-
│ │ mutex/agent │ │ → sessionId │ │ status: pending→done │ │
|
|
75
|
-
│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │
|
|
76
|
-
│ │
|
|
77
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
78
|
-
│ │ OverBridgeService (SDK wrapper) │ │
|
|
79
|
-
│ │ → BridgeProxy → HTTP POST http://[::1]:3099/mcp │ │
|
|
80
|
-
│ │ → Overmind MCP Server → HermesGatewayRunner → Gateway :8642 │ │
|
|
81
|
-
│ └─────────────────────────────────────────────────────────────┘ │
|
|
82
|
-
└──────────────────────────────────────────────────────────────────┘
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## Les 3 Couches de Persistance
|
|
88
|
-
|
|
89
|
-
| Couche | Stockage | TTL | Rôle |
|
|
90
|
-
|--------|----------|-----|------|
|
|
91
|
-
| **AgentRegistry** | In-memory (Map) | Session live | Mutex par agent (1 run à la fois), statut busy/idle/online |
|
|
92
|
-
| **SessionStore** | JSON file (`~/.overmind/bridge/sessions.json`) | 4h | Map `externalKey` → `sessionId` (multi-tenant: 1 session par utilisateur Discord) |
|
|
93
|
-
| **MessageLog** | PostgreSQL (`bridge_messages` table) | Permanent | Historique complet: from, to, prompt, response, status, timestamps |
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## AgentRegistry — État Live en Mémoire
|
|
98
|
-
|
|
99
|
-
**Source**: `src/bridge/AgentRegistry.ts`
|
|
100
|
-
|
|
101
|
-
Tracke l'état live de chaque agent avec un **mutex par agent** pour sérialiser les appels concurrents :
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
interface AgentLiveState {
|
|
105
|
-
name: string;
|
|
106
|
-
runner: RunnerType;
|
|
107
|
-
status: 'online' | 'offline' | 'busy' | 'idle';
|
|
108
|
-
pid?: number;
|
|
109
|
-
currentSessionId?: string; // Session en cours (si busy)
|
|
110
|
-
lastActivityAt: number;
|
|
111
|
-
totalRuns: number;
|
|
112
|
-
totalErrors: number;
|
|
113
|
-
a2aReceived: number; // Compteur A2A reçu
|
|
114
|
-
a2aSent: number; // Compteur A2A envoyé
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Mutex — 1 Run à la Fois par Agent
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
// Si agent "trader_btc" est déjà en train de tourner,
|
|
122
|
-
// le 2e appel attend que le 1er termine.
|
|
123
|
-
await this.registry.withLock('trader_btc', async () => {
|
|
124
|
-
this.registry.markBusy('trader_btc', sessionId);
|
|
125
|
-
const result = await this.service.runAgent({ ... });
|
|
126
|
-
this.registry.markIdle('trader_btc', !result.isError);
|
|
127
|
-
return result;
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Méthodes Disponibles
|
|
132
|
-
|
|
133
|
-
| Méthode | Description |
|
|
134
|
-
|---------|-------------|
|
|
135
|
-
| `register(name, runner)` | Enregistre l'agent dans le registry |
|
|
136
|
-
| `markBusy(name, sessionId)` | Marque comme occupé (run en cours) |
|
|
137
|
-
| `markIdle(name, success)` | Marque comme libre + incrémente compteurs |
|
|
138
|
-
| `markOnline(name)` | Marque comme en ligne |
|
|
139
|
-
| `markOffline(name)` | Marque comme hors ligne |
|
|
140
|
-
| `get(name)` | Retourne l'état live complet |
|
|
141
|
-
| `list({ status, runner })` | Liste filtrée des agents |
|
|
142
|
-
| `stats()` | Stats globales (total, busy, idle, online) |
|
|
143
|
-
| `incrementA2aSent(name)` | Incrémente le compteur A2A envoyé |
|
|
144
|
-
| `incrementA2aReceived(name)` | Incrémente le compteur A2A reçu |
|
|
145
|
-
| `prune(maxAgeMs)` | Nettoie les agents offline > 24h (auto toutes les 6h) |
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## SessionStore — Sessions Multi-Tenant
|
|
150
|
-
|
|
151
|
-
**Source**: `src/bridge/SessionStore.ts`
|
|
152
|
-
|
|
153
|
-
Le **multi-tenant** : chaque utilisateur externe a sa propre session persistante avec l'agent.
|
|
154
|
-
|
|
155
|
-
### Comment ça Marche
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
Utilisateur Discord A (!sniper analyse BTC)
|
|
159
|
-
→ externalKey = "discord_user_123"
|
|
160
|
-
→ SessionStore.get("discord_user_123", "sniperbot_analyst")
|
|
161
|
-
→ sessionId = "abc-123" (session restaurée!)
|
|
162
|
-
→ runAgent avec sessionId="abc-123"
|
|
163
|
-
→ SessionStore.set("discord_user_123", "sniperbot_analyst", "abc-123")
|
|
164
|
-
|
|
165
|
-
Utilisateur Discord B (!sniper analyse ETH)
|
|
166
|
-
→ externalKey = "discord_user_456"
|
|
167
|
-
→ SessionStore.get("discord_user_456", "sniperbot_analyst")
|
|
168
|
-
→ pas de session → nouvelle session
|
|
169
|
-
→ sessionId = "def-456"
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Persistence
|
|
173
|
-
|
|
174
|
-
```json
|
|
175
|
-
// ~/.overmind/bridge/sessions.json
|
|
176
|
-
{
|
|
177
|
-
"discord_user_123:sniperbot_analyst": {
|
|
178
|
-
"externalKey": "discord_user_123",
|
|
179
|
-
"agentName": "sniperbot_analyst",
|
|
180
|
-
"runner": "hermes",
|
|
181
|
-
"sessionId": "abc-123",
|
|
182
|
-
"context": { "lastTopic": "BTC", "language": "fr" },
|
|
183
|
-
"createdAt": "2026-07-09T18:00:00Z",
|
|
184
|
-
"updatedAt": "2026-07-09T18:05:00Z"
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### TTL et Cleanup
|
|
190
|
-
|
|
191
|
-
- TTL par défaut: **4h** (`sessionTtlMs: 4 * 60 * 60 * 1000`)
|
|
192
|
-
- Cleanup automatique toutes les **5min** (`cleanupIntervalMs`)
|
|
193
|
-
- Sessions expirées → supprimées du fichier
|
|
194
|
-
|
|
195
|
-
### Directives — L'Agent Peut Mettre à Jour Sa Session
|
|
196
|
-
|
|
197
|
-
Quand `enableDirectives: true`, l'agent peut émettre des directives dans sa réponse :
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
SESSION_ID: new-session-xyz → met à jour le sessionId
|
|
201
|
-
CONTEXT_UPDATE: {"lastTopic": "ETH"} → patch le contexte
|
|
202
|
-
BRIDGE_HINT: "Analyse terminée" → log seulement
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Le `DirectiveParser` (`src/bridge/DirectiveParser.ts`) parse ces directives, les applique au SessionStore, et les retire du texte visible par l'utilisateur.
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## MessageLog — Persistence Postgres
|
|
210
|
-
|
|
211
|
-
**Source**: `src/bridge/MessageLog.ts`
|
|
212
|
-
|
|
213
|
-
Historique permanent de tous les messages A2A et agent.run dans PostgreSQL.
|
|
214
|
-
|
|
215
|
-
### Schéma de Table
|
|
216
|
-
|
|
217
|
-
```sql
|
|
218
|
-
CREATE TABLE bridge_messages (
|
|
219
|
-
id UUID PRIMARY KEY,
|
|
220
|
-
from_agent VARCHAR(255), -- NULL si client externe
|
|
221
|
-
to_agent VARCHAR(255) NOT NULL,
|
|
222
|
-
runner VARCHAR(50) NOT NULL,
|
|
223
|
-
prompt TEXT NOT NULL,
|
|
224
|
-
response TEXT,
|
|
225
|
-
session_id VARCHAR(255),
|
|
226
|
-
status VARCHAR(20) DEFAULT 'pending', -- pending → running → done/failed/timeout
|
|
227
|
-
metadata JSONB,
|
|
228
|
-
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
229
|
-
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
CREATE INDEX idx_bridge_messages_to_agent ON bridge_messages(to_agent, created_at DESC);
|
|
233
|
-
CREATE INDEX idx_bridge_messages_from_agent ON bridge_messages(from_agent, created_at DESC);
|
|
234
|
-
CREATE INDEX idx_bridge_messages_status ON bridge_messages(status);
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Cycle de Vie d'un Message
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
agent.run appelé
|
|
241
|
-
→ messageLog.create() → status: "pending"
|
|
242
|
-
→ messageLog.markRunning() → status: "running"
|
|
243
|
-
→ agent termine...
|
|
244
|
-
→ succès: messageLog.markDone() → status: "done", response sauvegardée
|
|
245
|
-
→ erreur: messageLog.markFailed() → status: "failed", error sauvegardé
|
|
246
|
-
→ timeout: messageLog.markTimeout() → status: "timeout"
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Replay
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
# Rejouer un message (re-run avec le même prompt)
|
|
253
|
-
curl -X POST http://localhost:3100/rpc \
|
|
254
|
-
-d '{"jsonrpc":"2.0","id":1,"method":"message.replay","params":{"id":"7f3e8a1b-..."}}'
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## JSON-RPC 2.0 — Toutes les Méthodes
|
|
260
|
-
|
|
261
|
-
### Endpoint
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
POST /rpc
|
|
265
|
-
Content-Type: application/json
|
|
266
|
-
Authorization: Bearer <token> (optionnel si authToken configuré)
|
|
267
|
-
|
|
268
|
-
# Single request
|
|
269
|
-
{"jsonrpc":"2.0","id":1,"method":"agent.run","params":{...}}
|
|
270
|
-
|
|
271
|
-
# Batch (parallèle)
|
|
272
|
-
[
|
|
273
|
-
{"jsonrpc":"2.0","id":1,"method":"agent.run","params":{...}},
|
|
274
|
-
{"jsonrpc":"2.0","id":2,"method":"agent.list","params":{}}
|
|
275
|
-
]
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### Méthodes Disponibles (24 méthodes)
|
|
279
|
-
|
|
280
|
-
#### Agents (10 méthodes)
|
|
281
|
-
|
|
282
|
-
| Méthode | Params Requis | Params Optionnels | Description |
|
|
283
|
-
|---------|---------------|-------------------|-------------|
|
|
284
|
-
| `agent.run` | `agentName`, `runner`, `prompt` | `sessionId`, `path`, `model`, `mode`, `silent`, `externalKey`, `parseDirectives`, `metadata` | Lance un agent |
|
|
285
|
-
| `agent.a2a` | `fromAgent`, `toAgent`, `runner`, `prompt` | `model`, `path`, `metadata` | Agent A parle à Agent B |
|
|
286
|
-
| `agent.broadcast` | `fromAgent`, `runner`, `prompt` | `targets[]`, `race`, `agentTimeoutMs`, `model` | Fan-out global |
|
|
287
|
-
| `agent.pipeline` | `initiator`, `runner`, `prompt`, `steps[]` | `accumulateContext`, `totalTimeoutMs` | Chaîne A→B→C |
|
|
288
|
-
| `agent.fanout` | `fromAgent`, `runner`, `prompt`, `targets[]` | `mergeStrategy`, `agentTimeoutMs`, `model` | 1→N + merge |
|
|
289
|
-
| `agent.delegate` | `fromAgent`, `toAgent`, `runner`, `prompt` | `async`, `callbackUrl`, `model` | Fire-and-forget |
|
|
290
|
-
| `agent.query` | `fromAgent`, `runner`, `prompt`, `targets[]` | `agentTimeoutMs`, `model` | Query multi-agents |
|
|
291
|
-
| `agent.status` | `agentName` | `runner`, `action`, `sinceTimestamp`, `timeoutMs` | Status live |
|
|
292
|
-
| `agent.list` | — | `status`, `runner` | Liste des agents |
|
|
293
|
-
| `agent.kill` | `agentName` | `runner` | Kill un agent |
|
|
294
|
-
|
|
295
|
-
#### Messages (4 méthodes)
|
|
296
|
-
|
|
297
|
-
| Méthode | Params | Description |
|
|
298
|
-
|---------|--------|-------------|
|
|
299
|
-
| `message.history` | `toAgent?`, `fromAgent?`, `status?`, `limit?`, `offset?`, `sinceHours?` | Historique paginé |
|
|
300
|
-
| `message.get` | `id` (UUID) | Récupère un message |
|
|
301
|
-
| `message.replay` | `id` (UUID) | Rejoue un message |
|
|
302
|
-
| `message.stats` | — | Stats globales |
|
|
303
|
-
|
|
304
|
-
#### Sessions (4 méthodes)
|
|
305
|
-
|
|
306
|
-
| Méthode | Params | Description |
|
|
307
|
-
|---------|--------|-------------|
|
|
308
|
-
| `session.get` | `externalKey`, `agentName` | Session d'un utilisateur |
|
|
309
|
-
| `session.list` | — | Toutes les sessions |
|
|
310
|
-
| `session.delete` | `externalKey`, `agentName` | Supprime une session |
|
|
311
|
-
| `session.stats` | — | Stats des sessions |
|
|
312
|
-
|
|
313
|
-
#### Autres (6 méthodes)
|
|
314
|
-
|
|
315
|
-
| Méthode | Description |
|
|
316
|
-
|---------|-------------|
|
|
317
|
-
| `health.ping` | Liveness check |
|
|
318
|
-
| `webhook.sms` | Adapt + auto-dispatch webhook SMS |
|
|
319
|
-
| `GET /health` | Healthcheck enrichi (hors RPC) |
|
|
320
|
-
| `POST /webhook/:provider` | Webhook HTTP (voipms, twilio, discord) |
|
|
321
|
-
| `GET /f/:filename` | Static file serve |
|
|
322
|
-
| `OPTIONS *` | CORS preflight |
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
## A2A — Agent-to-Agent
|
|
327
|
-
|
|
328
|
-
### Principe
|
|
329
|
-
|
|
330
|
-
Agent A envoie un message à Agent B. Le bridge :
|
|
331
|
-
1. Enrichit le prompt avec un header A2A standardisé
|
|
332
|
-
2. Persiste le message dans MessageLog (from=A, to=B)
|
|
333
|
-
3. Incrémente les compteurs A2A (sent pour A, received pour B)
|
|
334
|
-
4. Exécute B sous mutex (sérialise les appels concurrents vers B)
|
|
335
|
-
5. Retourne la réponse de B à A
|
|
336
|
-
|
|
337
|
-
### Format du Prompt A2A
|
|
338
|
-
|
|
339
|
-
```
|
|
340
|
-
[A2A — Agent-to-Agent Message]
|
|
341
|
-
FROM: trader_btc
|
|
342
|
-
TO: analyst_eth
|
|
343
|
-
TIMESTAMP: 2026-07-09T18:30:00.000Z
|
|
344
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
345
|
-
Valide mon analyse : le BTC va casser les 100k dans 48h
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Exemple curl
|
|
349
|
-
|
|
350
|
-
```bash
|
|
351
|
-
curl -X POST http://localhost:3100/rpc \
|
|
352
|
-
-H "Content-Type: application/json" \
|
|
353
|
-
-d '{
|
|
354
|
-
"jsonrpc": "2.0",
|
|
355
|
-
"id": 1,
|
|
356
|
-
"method": "agent.a2a",
|
|
357
|
-
"params": {
|
|
358
|
-
"fromAgent": "trader_btc",
|
|
359
|
-
"toAgent": "analyst_eth",
|
|
360
|
-
"runner": "hermes",
|
|
361
|
-
"prompt": "Valide mon analyse : le BTC va casser les 100k dans 48h"
|
|
362
|
-
}
|
|
363
|
-
}'
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Réponse
|
|
367
|
-
|
|
368
|
-
```json
|
|
369
|
-
{
|
|
370
|
-
"jsonrpc": "2.0",
|
|
371
|
-
"id": 1,
|
|
372
|
-
"result": {
|
|
373
|
-
"messageId": "7f3e8a1b-...",
|
|
374
|
-
"from": "trader_btc",
|
|
375
|
-
"to": "analyst_eth",
|
|
376
|
-
"sessionId": "api-abc123",
|
|
377
|
-
"content": [{ "type": "text", "text": "Je valide avec conviction 75..." }],
|
|
378
|
-
"isError": false
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
## A2A Extended — Broadcast, Pipeline, Fanout, Delegate, Query
|
|
386
|
-
|
|
387
|
-
### 1. Broadcast — 1 → Tous (Fan-out Global)
|
|
388
|
-
|
|
389
|
-
Un agent envoie un message à tous les agents online (ou à une liste cible).
|
|
390
|
-
|
|
391
|
-
```bash
|
|
392
|
-
curl -X POST http://localhost:3100/rpc \
|
|
393
|
-
-d '{
|
|
394
|
-
"jsonrpc": "2.0", "id": 1,
|
|
395
|
-
"method": "agent.broadcast",
|
|
396
|
-
"params": {
|
|
397
|
-
"fromAgent": "commander",
|
|
398
|
-
"runner": "hermes",
|
|
399
|
-
"prompt": "Alerte marché : BTC drop 5%",
|
|
400
|
-
"race": false,
|
|
401
|
-
"agentTimeoutMs": 60000
|
|
402
|
-
}
|
|
403
|
-
}'
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
- `race: true` → retourne dès qu'un agent répond (premier qui gagne)
|
|
407
|
-
- `race: false` → attend tous les agents
|
|
408
|
-
- Si `targets` absent → tous les agents `online` (excluant l'émetteur)
|
|
409
|
-
|
|
410
|
-
### 2. Pipeline — Chaîne Séquentielle A→B→C
|
|
411
|
-
|
|
412
|
-
L'output de chaque step devient l'input du suivant.
|
|
413
|
-
|
|
414
|
-
```bash
|
|
415
|
-
curl -X POST http://localhost:3100/rpc \
|
|
416
|
-
-d '{
|
|
417
|
-
"jsonrpc": "2.0", "id": 1,
|
|
418
|
-
"method": "agent.pipeline",
|
|
419
|
-
"params": {
|
|
420
|
-
"initiator": "commander",
|
|
421
|
-
"runner": "hermes",
|
|
422
|
-
"prompt": "Analyse le marché BTC actuel",
|
|
423
|
-
"steps": [
|
|
424
|
-
{ "agentName": "data_collector", "promptPrefix": "Récupère les données:" },
|
|
425
|
-
{ "agentName": "analyst", "promptPrefix": "Analyse ces données:" },
|
|
426
|
-
{ "agentName": "strategist", "promptPrefix": "Propose une stratégie:" }
|
|
427
|
-
],
|
|
428
|
-
"accumulateContext": true,
|
|
429
|
-
"totalTimeoutMs": 3600000
|
|
430
|
-
}
|
|
431
|
-
}'
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
- `accumulateContext: true` → chaque step reçoit TOUS les outputs précédents
|
|
435
|
-
- `accumulateContext: false` → chaque step ne reçoit que le dernier output
|
|
436
|
-
- Timeout global avec deadline check à chaque step
|
|
437
|
-
|
|
438
|
-
### 3. Fanout — 1 → N Parallèle + Merge
|
|
439
|
-
|
|
440
|
-
Un agent demande à plusieurs agents en parallèle, puis merge les résultats.
|
|
441
|
-
|
|
442
|
-
```bash
|
|
443
|
-
curl -X POST http://localhost:3100/rpc \
|
|
444
|
-
-d '{
|
|
445
|
-
"jsonrpc": "2.0", "id": 1,
|
|
446
|
-
"method": "agent.fanout",
|
|
447
|
-
"params": {
|
|
448
|
-
"fromAgent": "commander",
|
|
449
|
-
"runner": "hermes",
|
|
450
|
-
"prompt": "Quelle stratégie pour le BTC?",
|
|
451
|
-
"targets": ["analyst_1", "analyst_2", "analyst_3"],
|
|
452
|
-
"mergeStrategy": "best"
|
|
453
|
-
}
|
|
454
|
-
}'
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
Stratégies de merge :
|
|
458
|
-
|
|
459
|
-
| Stratégie | Description |
|
|
460
|
-
|-----------|-------------|
|
|
461
|
-
| `concat` | Concatène toutes les réponses (défaut) |
|
|
462
|
-
| `best` | Sélectionne la plus longue réponse |
|
|
463
|
-
| `vote` | Vote par bucket de taille (100 chars) |
|
|
464
|
-
| `first_success` | Premier qui réussit |
|
|
465
|
-
|
|
466
|
-
### 4. Delegate — Fire-and-Forget Async
|
|
467
|
-
|
|
468
|
-
Délègue une tâche à un agent de façon asynchrone. Retourne immédiatement avec un `taskId`.
|
|
469
|
-
|
|
470
|
-
```bash
|
|
471
|
-
curl -X POST http://localhost:3100/rpc \
|
|
472
|
-
-d '{
|
|
473
|
-
"jsonrpc": "2.0", "id": 1,
|
|
474
|
-
"method": "agent.delegate",
|
|
475
|
-
"params": {
|
|
476
|
-
"fromAgent": "commander",
|
|
477
|
-
"toAgent": "researcher",
|
|
478
|
-
"runner": "hermes",
|
|
479
|
-
"prompt": "Recherche l'historique des crashes BTC 2024",
|
|
480
|
-
"async": true,
|
|
481
|
-
"callbackUrl": "https://myapp.com/webhook/agent-done"
|
|
482
|
-
}
|
|
483
|
-
}'
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
- `async: true` → retourne immédiatement avec `taskId`
|
|
487
|
-
- `callbackUrl` → POST le résultat quand l'agent termine
|
|
488
|
-
- L'agent peut être suivi via `agent.status(agentName: "researcher")`
|
|
489
|
-
|
|
490
|
-
### 5. Query — Multi-Agent Read-Only
|
|
491
|
-
|
|
492
|
-
Comme fanout mais optimisé pour des réponses courtes et rapides.
|
|
493
|
-
|
|
494
|
-
```bash
|
|
495
|
-
curl -X POST http://localhost:3100/rpc \
|
|
496
|
-
-d '{
|
|
497
|
-
"jsonrpc": "2.0", "id": 1,
|
|
498
|
-
"method": "agent.query",
|
|
499
|
-
"params": {
|
|
500
|
-
"fromAgent": "commander",
|
|
501
|
-
"runner": "hermes",
|
|
502
|
-
"prompt": "Prix actuel du BTC?",
|
|
503
|
-
"targets": ["price_bot_1", "price_bot_2"],
|
|
504
|
-
"agentTimeoutMs": 30000
|
|
505
|
-
}
|
|
506
|
-
}'
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
## Comment Construire un Agent Persistant (comme discord_llm)
|
|
512
|
-
|
|
513
|
-
`discord_llm` est l'exemple canonique d'un client persistant du bridge. Voici le pattern complet :
|
|
514
|
-
|
|
515
|
-
### Architecture discord_llm
|
|
516
|
-
|
|
517
|
-
```
|
|
518
|
-
┌─────────────────────────────────────────────────────────┐
|
|
519
|
-
│ discord_llm/ (package: discord-claude-bridge v3.5.0) │
|
|
520
|
-
│ │
|
|
521
|
-
│ startpipeline.ts → PipelineManager │
|
|
522
|
-
│ ├── spawn overmind-bridge.ts (Express :3001) │
|
|
523
|
-
│ │ ├── OverBridgeService (SDK wrapper) │
|
|
524
|
-
│ │ ├── BridgeProxy → HTTP [::1]:3099/mcp │
|
|
525
|
-
│ │ ├── SessionStore (multi-tenant, JSON file) │
|
|
526
|
-
│ │ └── WebhookAdapter (voipms, twilio, discord) │
|
|
527
|
-
│ │ │
|
|
528
|
-
│ └── spawn discord-bot.ts (Discord.js) │
|
|
529
|
-
│ ├── reçoit !sniper <message> │
|
|
530
|
-
│ └── POST localhost:3001/send │
|
|
531
|
-
│ → OverBridgeService.runAgentForDiscord() │
|
|
532
|
-
│ → BridgeProxy → MCP :3099 │
|
|
533
|
-
│ → HermesGatewayRunner → Gateway :8642 │
|
|
534
|
-
│ → réponse → Discord │
|
|
535
|
-
└─────────────────────────────────────────────────────────┘
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
### Étape 1 : Importer le SDK Overmind
|
|
539
|
-
|
|
540
|
-
```typescript
|
|
541
|
-
// Import depuis overmind-mcp (package npm ou file:../Workflow)
|
|
542
|
-
import {
|
|
543
|
-
OverBridgeService, // SDK haut niveau (runAgent, sessions, heartbeat)
|
|
544
|
-
SessionStore, // Multi-tenant (externalKey → sessionId)
|
|
545
|
-
WebhookAdapter, // Adaptation webhooks (SMS, Discord, Twilio)
|
|
546
|
-
BridgeProxy, // Low-level MCP transport (HTTP JSON-RPC)
|
|
547
|
-
} from 'overmind-mcp/bridge';
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
### Étape 2 : Configurer le Bridge
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
const OVERMIND_MCP_URL = process.env.OVERMIND_MCP_URL || 'http://localhost:3099/mcp';
|
|
554
|
-
const AGENT_NAME = process.env.BRIDGE_AGENT || 'sniperbot_analyst';
|
|
555
|
-
const RUNNER = 'hermes';
|
|
556
|
-
|
|
557
|
-
// Low-level proxy → parle au MCP server
|
|
558
|
-
const proxy = new BridgeProxy({
|
|
559
|
-
mcpUrl: OVERMIND_MCP_URL,
|
|
560
|
-
defaultTimeoutMs: 60_000,
|
|
561
|
-
agentTimeoutMs: 2_700_000, // 45 min
|
|
562
|
-
maxRetries: 2,
|
|
563
|
-
retryDelayMs: 2_000,
|
|
564
|
-
}, undefined, logger);
|
|
565
|
-
|
|
566
|
-
// High-level service → session continuity + retry + circuit breaker
|
|
567
|
-
const service = new OverBridgeService({
|
|
568
|
-
mcpUrl: OVERMIND_MCP_URL,
|
|
569
|
-
defaultTimeoutMs: 60_000,
|
|
570
|
-
agentTimeoutMs: 2_700_000,
|
|
571
|
-
maxRetries: 2,
|
|
572
|
-
retryDelayMs: 2_000,
|
|
573
|
-
}, logger);
|
|
574
|
-
|
|
575
|
-
// Multi-tenant sessions
|
|
576
|
-
const sessionStore = new SessionStore({
|
|
577
|
-
persistPath: '~/.overmind/bridge/sessions.json',
|
|
578
|
-
ttlMs: 4 * 60 * 60 * 1000, // 4h
|
|
579
|
-
cleanupIntervalMs: 5 * 60 * 1000, // 5min
|
|
580
|
-
}, logger);
|
|
581
|
-
|
|
582
|
-
await sessionStore.init();
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### Étape 3 : Exposer les Endpoints HTTP
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
import express from 'express';
|
|
589
|
-
const app = express();
|
|
590
|
-
app.use(express.json({ limit: '10mb' }));
|
|
591
|
-
|
|
592
|
-
// ─── Route legacy (Discord bot) ─────────────────────────
|
|
593
|
-
app.post('/send', async (req, res) => {
|
|
594
|
-
const { message, userId, username, channelId } = req.body;
|
|
595
|
-
|
|
596
|
-
// Lance l'agent avec contexte Discord
|
|
597
|
-
const result = await service.runAgentForDiscord(
|
|
598
|
-
AGENT_NAME, RUNNER, message,
|
|
599
|
-
{ channelId, userId, username }
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
if (result.isError) {
|
|
603
|
-
return res.status(500).json({ error: result.content.map(c => c.text).join('\n') });
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
res.json({
|
|
607
|
-
result: result.content.map(c => c.text).join('\n'),
|
|
608
|
-
session_id: result.sessionId || service.sessionId,
|
|
609
|
-
});
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
// ─── JSON-RPC 2.0 endpoint ──────────────────────────────
|
|
613
|
-
app.post('/rpc', async (req, res) => {
|
|
614
|
-
const { method, params, id } = req.body;
|
|
615
|
-
|
|
616
|
-
switch (method) {
|
|
617
|
-
case 'agent.run':
|
|
618
|
-
// Résolution sessionId via SessionStore
|
|
619
|
-
let sessionId = params.sessionId;
|
|
620
|
-
if (!sessionId && params.externalKey) {
|
|
621
|
-
const stored = sessionStore.get(params.externalKey, params.agentName);
|
|
622
|
-
if (stored) sessionId = stored.sessionId;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
const result = await service.runAgent({
|
|
626
|
-
agentName: params.agentName,
|
|
627
|
-
runner: params.runner,
|
|
628
|
-
prompt: params.prompt,
|
|
629
|
-
sessionId,
|
|
630
|
-
model: params.model,
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
// Sauvegarde sessionId pour cet utilisateur
|
|
634
|
-
if (params.externalKey && result.sessionId) {
|
|
635
|
-
sessionStore.set({
|
|
636
|
-
externalKey: params.externalKey,
|
|
637
|
-
agentName: params.agentName,
|
|
638
|
-
runner: params.runner,
|
|
639
|
-
sessionId: result.sessionId,
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
return res.json({ jsonrpc: '2.0', id, result: {
|
|
644
|
-
sessionId: result.sessionId,
|
|
645
|
-
content: result.content,
|
|
646
|
-
isError: result.isError,
|
|
647
|
-
}});
|
|
648
|
-
|
|
649
|
-
case 'agent.a2a':
|
|
650
|
-
const enrichedPrompt = [
|
|
651
|
-
`[A2A — Agent-to-Agent Message]`,
|
|
652
|
-
`FROM: ${params.fromAgent}`,
|
|
653
|
-
`TO: ${params.toAgent}`,
|
|
654
|
-
`TIMESTAMP: ${new Date().toISOString()}`,
|
|
655
|
-
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
656
|
-
params.prompt,
|
|
657
|
-
].join('\n');
|
|
658
|
-
|
|
659
|
-
const a2aResult = await service.runAgent({
|
|
660
|
-
agentName: params.toAgent,
|
|
661
|
-
runner: params.runner,
|
|
662
|
-
prompt: enrichedPrompt,
|
|
663
|
-
model: params.model,
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
return res.json({ jsonrpc: '2.0', id, result: {
|
|
667
|
-
from: params.fromAgent,
|
|
668
|
-
to: params.toAgent,
|
|
669
|
-
sessionId: a2aResult.sessionId,
|
|
670
|
-
content: a2aResult.content,
|
|
671
|
-
isError: a2aResult.isError,
|
|
672
|
-
}});
|
|
673
|
-
|
|
674
|
-
case 'agent.list':
|
|
675
|
-
const agents = await proxy.call('list_agents', { details: params.details });
|
|
676
|
-
return res.json({ jsonrpc: '2.0', id, result: agents.result });
|
|
677
|
-
|
|
678
|
-
case 'session.get':
|
|
679
|
-
return res.json({ jsonrpc: '2.0', id, result: {
|
|
680
|
-
session: sessionStore.get(params.externalKey, params.agentName) ?? null,
|
|
681
|
-
}});
|
|
682
|
-
|
|
683
|
-
case 'session.list':
|
|
684
|
-
return res.json({ jsonrpc: '2.0', id, result: {
|
|
685
|
-
sessions: sessionStore.list(),
|
|
686
|
-
stats: sessionStore.stats(),
|
|
687
|
-
}});
|
|
688
|
-
|
|
689
|
-
case 'health.ping':
|
|
690
|
-
return res.json({ jsonrpc: '2.0', id, result: { pong: true, ts: Date.now() }});
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
// ─── Health ─────────────────────────────────────────────
|
|
695
|
-
app.get('/health', async (_req, res) => {
|
|
696
|
-
let mcpStatus = 'unknown';
|
|
697
|
-
try {
|
|
698
|
-
const health = await proxy.healthCheck();
|
|
699
|
-
mcpStatus = health.status;
|
|
700
|
-
} catch { mcpStatus = 'offline'; }
|
|
701
|
-
res.json({
|
|
702
|
-
status: 'ok',
|
|
703
|
-
version: '5.1.0',
|
|
704
|
-
mcp: { url: OVERMIND_MCP_URL, status: mcpStatus },
|
|
705
|
-
sessions: sessionStore.stats(),
|
|
706
|
-
});
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
app.listen(3001, () => logger.info('Bridge on :3001'));
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
### Étape 4 : Démarrer le Pipeline
|
|
713
|
-
|
|
714
|
-
```typescript
|
|
715
|
-
// startpipeline.ts — orchestre bridge + bot
|
|
716
|
-
class PipelineManager {
|
|
717
|
-
async start() {
|
|
718
|
-
// 1. Démarrer le bridge (HTTP server)
|
|
719
|
-
this.bridgeProcess = spawn('node', ['dist/overmind-bridge.js']);
|
|
720
|
-
|
|
721
|
-
// 2. Attendre qu'il soit prêt
|
|
722
|
-
await this.waitForBridge(); // GET /health jusqu'à 200
|
|
723
|
-
|
|
724
|
-
// 3. Démarrer le bot Discord
|
|
725
|
-
this.discordProcess = spawn('node', ['dist/discord-bot.js']);
|
|
726
|
-
|
|
727
|
-
// 4. Auto-restart du bot si crash (max 3 tentatives)
|
|
728
|
-
this.discordProcess.on('exit', (code) => {
|
|
729
|
-
if (!this.isShuttingDown) this.restartDiscordBot();
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
### Étape 5 : Configurer le .env
|
|
736
|
-
|
|
737
|
-
```bash
|
|
738
|
-
# discord_llm/.env
|
|
739
|
-
CLAUDE_SERVER_PORT=3001 # Port du bridge
|
|
740
|
-
RUNNER=hermes # Runner Overmind
|
|
741
|
-
BRIDGE_AGENT=sniperbot_analyst # Agent par défaut
|
|
742
|
-
OVERMIND_MCP_URL=http://[::1]:3099/mcp # MCP server
|
|
743
|
-
DISCORD_BOT_TOKEN=*** # Token Discord
|
|
744
|
-
BOT_PREFIX=!sniper # Préfixe commandes
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
---
|
|
748
|
-
|
|
749
|
-
## CLI overmind-bridge
|
|
750
|
-
|
|
751
|
-
Le CLI `overmind-bridge` (`src/bin/overmind-bridge.ts`) permet de piloter le bridge sans écrire de code.
|
|
752
|
-
|
|
753
|
-
### Démarrer le serveur
|
|
754
|
-
|
|
755
|
-
```bash
|
|
756
|
-
overmind-bridge server --port 3100
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
### Appels one-shot (8 sources de prompt)
|
|
760
|
-
|
|
761
|
-
```bash
|
|
762
|
-
# 1. Flag direct
|
|
763
|
-
overmind-bridge call agent.run --agent scout --runner hermes --prompt "Analyse BTC"
|
|
764
|
-
|
|
765
|
-
# 2. Stdin
|
|
766
|
-
echo "Analyse BTC" | overmind-bridge call agent.run --agent scout --runner hermes --prompt-stdin
|
|
767
|
-
|
|
768
|
-
# 3. Fichier
|
|
769
|
-
overmind-bridge call agent.run --agent scout --runner hermes --prompt-file ./brief.txt
|
|
770
|
-
|
|
771
|
-
# 4. Fichier + variables
|
|
772
|
-
overmind-bridge call agent.run --agent scout --runner hermes \
|
|
773
|
-
--prompt-file ./brief.txt --var ticker=BTC --var timeframe=4h
|
|
774
|
-
|
|
775
|
-
# 5-8. env-var, scenario, pipe, multi-file (voir --help)
|
|
776
|
-
```
|
|
777
|
-
|
|
778
|
-
### A2A via CLI
|
|
779
|
-
|
|
780
|
-
```bash
|
|
781
|
-
overmind-bridge call agent.a2a \
|
|
782
|
-
--from scout --to analyst --runner hermes \
|
|
783
|
-
--prompt "Valide mon analyse"
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
### Scénarios multi-agents
|
|
787
|
-
|
|
788
|
-
```bash
|
|
789
|
-
# JSON scenario file
|
|
790
|
-
overmind-bridge scenario ./workflow.json --var ticker=BTC
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
### Status et Health
|
|
794
|
-
|
|
795
|
-
```bash
|
|
796
|
-
# Status de tous les agents
|
|
797
|
-
overmind-bridge status
|
|
798
|
-
|
|
799
|
-
# Health du serveur
|
|
800
|
-
overmind-bridge health
|
|
801
|
-
|
|
802
|
-
# Replay un message
|
|
803
|
-
overmind-bridge replay --id 7f3e8a1b-...
|
|
804
|
-
|
|
805
|
-
# Sessions
|
|
806
|
-
overmind-bridge sessions list
|
|
807
|
-
overmind-bridge sessions get --key "+141****7735" --agent pdf_bon_travail
|
|
808
|
-
overmind-bridge sessions rm --key "+141****7735" --agent pdf_bon_travail
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
---
|
|
812
|
-
|
|
813
|
-
## Démarrer le Serveur Bridge
|
|
814
|
-
|
|
815
|
-
### Option A : OverBridgeServer natif (port 3100)
|
|
816
|
-
|
|
817
|
-
```typescript
|
|
818
|
-
import { OverBridgeServer, OverBridgeService, loadMessageLogConfigFromEnv } from 'overmind-mcp/bridge';
|
|
819
|
-
|
|
820
|
-
const service = new OverBridgeService({
|
|
821
|
-
mcpUrl: 'http://[::1]:3099/mcp',
|
|
822
|
-
defaultTimeoutMs: 60_000,
|
|
823
|
-
agentTimeoutMs: 2_700_000,
|
|
824
|
-
}, logger);
|
|
825
|
-
|
|
826
|
-
const server = new OverBridgeServer(service, {
|
|
827
|
-
port: 3100,
|
|
828
|
-
host: '127.0.0.1',
|
|
829
|
-
postgres: loadMessageLogConfigFromEnv(),
|
|
830
|
-
enableMessageLog: true, // Persistence Postgres
|
|
831
|
-
enableSessionStore: true, // Multi-tenant sessions
|
|
832
|
-
enableDirectives: true, // SESSION_ID, CONTEXT_UPDATE directives
|
|
833
|
-
enableWebhooks: true, // /webhook/:provider
|
|
834
|
-
sessionTtlMs: 4 * 60 * 60 * 1000, // 4h
|
|
835
|
-
rateLimitMax: 100, // 100 req/min par IP
|
|
836
|
-
allowedOrigins: ['*'],
|
|
837
|
-
sanitizeJson: true, // Windows path repair
|
|
838
|
-
});
|
|
839
|
-
|
|
840
|
-
await server.start();
|
|
841
|
-
// POST http://127.0.0.1:3100/rpc (JSON-RPC 2.0)
|
|
842
|
-
// GET http://127.0.0.1:3100/health
|
|
843
|
-
// POST http://127.0.0.1:3100/webhook/:provider
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
### Option B : Bridge léger Express (comme discord_llm)
|
|
847
|
-
|
|
848
|
-
Pour un client qui n'a pas besoin de Postgres MessageLog mais veut les sessions multi-tenant :
|
|
849
|
-
|
|
850
|
-
```typescript
|
|
851
|
-
import express from 'express';
|
|
852
|
-
import { OverBridgeService, SessionStore, BridgeProxy } from 'overmind-mcp/bridge';
|
|
853
|
-
|
|
854
|
-
const app = express();
|
|
855
|
-
app.use(express.json());
|
|
856
|
-
|
|
857
|
-
const service = new OverBridgeService({ mcpUrl: 'http://[::1]:3099/mcp' });
|
|
858
|
-
const sessions = new SessionStore({ persistPath: './sessions.json' });
|
|
859
|
-
await sessions.init();
|
|
860
|
-
|
|
861
|
-
// POST /send, POST /rpc, GET /health...
|
|
862
|
-
app.listen(3001);
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
### Option C : CLI direct
|
|
866
|
-
|
|
867
|
-
```bash
|
|
868
|
-
# Démarrer le serveur OverBridgeServer complet
|
|
869
|
-
overmind-bridge server --port 3100 --enable-message-log --enable-sessions --enable-webhooks
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
---
|
|
873
|
-
|
|
874
|
-
## Configurations du OverBridgeServer
|
|
875
|
-
|
|
876
|
-
| Config | Default | Description |
|
|
877
|
-
|--------|---------|-------------|
|
|
878
|
-
| `port` | 3100 | Port HTTP |
|
|
879
|
-
| `host` | 127.0.0.1 | Host d'écoute |
|
|
880
|
-
| `postgres` | — | Config Postgres pour MessageLog |
|
|
881
|
-
| `enableMessageLog` | false | Persistence des messages en DB |
|
|
882
|
-
| `enableSessionStore` | false | Sessions multi-tenant |
|
|
883
|
-
| `enableDirectives` | false | Parse SESSION_ID, CONTEXT_UPDATE |
|
|
884
|
-
| `enableWebhooks` | false | Auto-mount /webhook/:provider |
|
|
885
|
-
| `authToken` | — | Bearer token auth |
|
|
886
|
-
| `healthCheckIntervalMs` | 30000 | Heartbeat vers MCP |
|
|
887
|
-
| `sessionTtlMs` | 14400000 (4h) | TTL sessions |
|
|
888
|
-
| `sessionStorePath` | ~/.overmind/bridge/sessions.json | Fichier persistence |
|
|
889
|
-
| `jsonBodyLimit` | 10mb | Limite body |
|
|
890
|
-
| `sanitizeJson` | false | Repair JSON Windows paths |
|
|
891
|
-
| `allowedOrigins` | localhost:3000,5173 | CORS |
|
|
892
|
-
| `rateLimitMax` | 100 | Max req/min par IP |
|