slashvibe-mcp 0.4.2 → 0.4.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.
Files changed (100) hide show
  1. package/package.json +30 -8
  2. package/version.json +4 -7
  3. package/analytics.js +0 -131
  4. package/auto-update.js +0 -125
  5. package/bridges/agent-gateway.js +0 -351
  6. package/bridges/bridge-monitor.js +0 -399
  7. package/bridges/discord-bot.js +0 -421
  8. package/bridges/farcaster.js +0 -299
  9. package/bridges/telegram.js +0 -261
  10. package/bridges/webhook-health.js +0 -416
  11. package/bridges/webhook-server.js +0 -461
  12. package/bridges/whatsapp.js +0 -441
  13. package/bridges/x-webhook.js +0 -406
  14. package/discord.js +0 -170
  15. package/eslint.config.js +0 -54
  16. package/intelligence/patterns.js +0 -656
  17. package/intelligence/proactive.js +0 -359
  18. package/memory.js +0 -171
  19. package/migrate-v2.js +0 -72
  20. package/post-install.js +0 -141
  21. package/protocol/index.js +0 -536
  22. package/protocol/telegram-commands.js +0 -198
  23. package/scripts/publish-registry.sh +0 -58
  24. package/store/profiles.js +0 -291
  25. package/store/reservations.js +0 -314
  26. package/store/skills.js +0 -376
  27. package/test-skills-bootstrap.js +0 -20
  28. package/test-v2-integration.js +0 -385
  29. package/tools/_archive/_connection-queue.js +0 -246
  30. package/tools/_archive/_discovery-enhanced.js +0 -285
  31. package/tools/_archive/_discovery.js +0 -341
  32. package/tools/_archive/_proactive-discovery.js +0 -288
  33. package/tools/_archive/admin-inbox.js +0 -213
  34. package/tools/_archive/agents.js +0 -122
  35. package/tools/_archive/artifact-create.js +0 -237
  36. package/tools/_archive/artifact-view.js +0 -174
  37. package/tools/_archive/away.js +0 -68
  38. package/tools/_archive/back.js +0 -51
  39. package/tools/_archive/bye.js +0 -50
  40. package/tools/_archive/consent.js +0 -114
  41. package/tools/_archive/context.js +0 -131
  42. package/tools/_archive/crossword.js +0 -370
  43. package/tools/_archive/doctor.js +0 -452
  44. package/tools/_archive/drawing.js +0 -324
  45. package/tools/_archive/echo.js +0 -218
  46. package/tools/_archive/feed.js +0 -199
  47. package/tools/_archive/follow.js +0 -101
  48. package/tools/_archive/forget.js +0 -116
  49. package/tools/_archive/game.js +0 -455
  50. package/tools/_archive/handoff.js +0 -237
  51. package/tools/_archive/idea.js +0 -205
  52. package/tools/_archive/insights.js +0 -123
  53. package/tools/_archive/invite.js +0 -215
  54. package/tools/_archive/migrate.js +0 -159
  55. package/tools/_archive/multiplayer-game.js +0 -285
  56. package/tools/_archive/mute.js +0 -98
  57. package/tools/_archive/notifications.js +0 -425
  58. package/tools/_archive/observe.js +0 -197
  59. package/tools/_archive/onboarding.js +0 -144
  60. package/tools/_archive/open.js +0 -120
  61. package/tools/_archive/party-game.js +0 -325
  62. package/tools/_archive/patterns.js +0 -80
  63. package/tools/_archive/ping.js +0 -49
  64. package/tools/_archive/presence-agent.js +0 -167
  65. package/tools/_archive/presence-data.js +0 -102
  66. package/tools/_archive/react.js +0 -113
  67. package/tools/_archive/recall.js +0 -142
  68. package/tools/_archive/release.js +0 -85
  69. package/tools/_archive/remember.js +0 -84
  70. package/tools/_archive/report.js +0 -109
  71. package/tools/_archive/request.js +0 -211
  72. package/tools/_archive/reservations.js +0 -116
  73. package/tools/_archive/reserve.js +0 -111
  74. package/tools/_archive/session-fork.js +0 -97
  75. package/tools/_archive/session-resume.js +0 -186
  76. package/tools/_archive/session-save.js +0 -109
  77. package/tools/_archive/settings.js +0 -131
  78. package/tools/_archive/skills-exchange.js +0 -341
  79. package/tools/_archive/social-inbox.js +0 -431
  80. package/tools/_archive/social-post.js +0 -323
  81. package/tools/_archive/solo-game.js +0 -398
  82. package/tools/_archive/submit.js +0 -100
  83. package/tools/_archive/suggest-tags.js +0 -187
  84. package/tools/_archive/summarize.js +0 -166
  85. package/tools/_archive/tag-suggestions.js +0 -256
  86. package/tools/_archive/test.js +0 -182
  87. package/tools/_archive/tictactoe.js +0 -155
  88. package/tools/_archive/token.js +0 -103
  89. package/tools/_archive/update.js +0 -104
  90. package/tools/_archive/watch.js +0 -177
  91. package/tools/_archive/wordassociation.js +0 -250
  92. package/tools/_archive/workshop-buddy.js +0 -393
  93. package/tools/_archive/x-mentions.js +0 -83
  94. package/tools/_archive/x-reply.js +0 -81
  95. package/tools/_experimental/l2-bridge.js +0 -272
  96. package/tools/_experimental/l2-status.js +0 -215
  97. package/tools/_experimental/l2.js +0 -206
  98. package/tools/_experimental/shipback.js +0 -327
  99. package/twitter.js +0 -203
  100. package/webhook-runner.js +0 -132
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slashvibe-mcp",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "mcpName": "io.github.VibeCodingInc/vibe-mcp",
5
5
  "description": "Social MCP server - DMs, presence, discovery, and games for AI-assisted developers. Works with Claude Code, Cursor, VS Code, Windsurf, and any MCP client.",
6
6
  "main": "index.js",
@@ -49,15 +49,37 @@
49
49
  "node": ">=18.0.0"
50
50
  },
51
51
  "files": [
52
- "*.js",
52
+ "index.js",
53
+ "config.js",
54
+ "notify.js",
55
+ "presence.js",
56
+ "crypto.js",
57
+ "debug.js",
58
+ "prompts.js",
59
+ "notification-emitter.js",
53
60
  "version.json",
54
- "tools/",
55
- "store/",
56
- "protocol/",
57
- "intelligence/",
58
- "bridges/",
61
+ "tools/_shared/",
62
+ "tools/_actions.js",
63
+ "tools/start.js",
64
+ "tools/init.js",
65
+ "tools/who.js",
66
+ "tools/dm.js",
67
+ "tools/inbox.js",
68
+ "tools/status.js",
69
+ "tools/ship.js",
70
+ "tools/discover.js",
71
+ "tools/help.js",
72
+ "store/index.js",
73
+ "store/api.js",
74
+ "store/local.js",
75
+ "store/sqlite.js",
76
+ "store/sessions.js",
77
+ "intelligence/index.js",
78
+ "intelligence/infer.js",
79
+ "intelligence/serendipity.js",
59
80
  "lib/",
60
- "scripts/",
81
+ "scripts/sync-version.js",
82
+ "scripts/install-editors.js",
61
83
  "README.md"
62
84
  ],
63
85
  "dependencies": {},
package/version.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
- "version": "0.4.2",
2
+ "version": "0.4.3",
3
3
  "updated": "2026-02-12",
4
- "changelog": "Slim start/who/actions, archive dead tools, rewrite README",
4
+ "changelog": "Trim npm package: 132→34 files, 816KB→195KB",
5
5
  "features": [
6
- "start.js: 297→105 lines (removed auto-update, version check)",
7
- "who.js: 321175 lines (kept intelligence, removed bloat)",
8
- "_actions.js: 486→88 lines (stripped dead generators)",
9
- "README.md: reflects 8-tool reality",
10
- "Archived 6 unregistered tool files"
6
+ "Explicit files whitelist only ships what GTM tools need",
7
+ "Package size: 213KB54KB compressed"
11
8
  ],
12
9
  "breaking": false,
13
10
  "updateUrl": "https://www.slashvibe.dev/api/version"
package/analytics.js DELETED
@@ -1,131 +0,0 @@
1
- /**
2
- * Analytics — Retention event tracking from MCP server
3
- *
4
- * Logs events to the /api/analytics/event endpoint for measuring
5
- * user engagement and retention funnel performance.
6
- *
7
- * Usage:
8
- * const analytics = require('./analytics');
9
- * analytics.track('empty_inbox_action', { action: 'discover', source: 'inbox' });
10
- */
11
-
12
- const config = require('./config');
13
-
14
- const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
15
-
16
- /**
17
- * Track an analytics event (fire and forget)
18
- * @param {string} eventType - Event type (from valid types in api/lib/events.js)
19
- * @param {object} data - Additional event data
20
- */
21
- async function track(eventType, data = {}) {
22
- const handle = config.getHandle();
23
- if (!handle) return; // Skip if not initialized
24
-
25
- try {
26
- // Fire and forget - don't await or block
27
- fetch(`${API_URL}/api/analytics/events`, {
28
- method: 'POST',
29
- headers: { 'Content-Type': 'application/json' },
30
- body: JSON.stringify({
31
- type: eventType,
32
- handle,
33
- data: {
34
- ...data,
35
- client: 'mcp-server',
36
- timestamp: Date.now()
37
- }
38
- })
39
- }).catch(() => {}); // Silently ignore errors
40
- } catch (e) {
41
- // Analytics should never block or fail user flows
42
- }
43
- }
44
-
45
- /**
46
- * Track empty inbox interaction
47
- * @param {string} action - Which action was taken (or 'none' if user closed)
48
- * @param {object} context - Context about the state (hadOnboardingTask, hadRecentShips, etc.)
49
- */
50
- function trackEmptyInbox(action, context = {}) {
51
- // Track that user reached empty inbox state
52
- track('empty_inbox_reached', {
53
- hadRecentThreads: context.recentThreads?.length > 0,
54
- hadOnboardingTask: !!context.onboardingTask,
55
- hadRecentShips: context.recentShips?.length > 0
56
- });
57
-
58
- // If an action was taken, track it
59
- if (action && action !== 'none') {
60
- track('empty_inbox_action', {
61
- action,
62
- ...context
63
- });
64
- }
65
- }
66
-
67
- /**
68
- * Track lurk mode state change
69
- * @param {boolean} enabled - Whether lurk mode was enabled or disabled
70
- */
71
- function trackLurkMode(enabled) {
72
- track(enabled ? 'lurk_mode_enabled' : 'lurk_mode_disabled', {});
73
- }
74
-
75
- /**
76
- * Track onboarding task completion
77
- * @param {string} taskId - The task that was completed
78
- */
79
- function trackOnboardingTask(taskId) {
80
- track('onboarding_task_completed', { taskId });
81
- }
82
-
83
- /**
84
- * Track discovery initiation
85
- * @param {string} source - Where discovery was initiated from (inbox, start, etc.)
86
- */
87
- function trackDiscovery(source) {
88
- track('discovery_initiated', { source });
89
- }
90
-
91
- /**
92
- * Track session lifecycle
93
- * @param {string} event - 'started' or 'ended'
94
- * @param {object} sessionData - Session metrics (duration, actions, etc.)
95
- */
96
- function trackSession(event, sessionData = {}) {
97
- track(event === 'started' ? 'session_started' : 'session_ended', sessionData);
98
- }
99
-
100
- /**
101
- * Track editor install via universal installer
102
- * @param {string} editor - Editor name configured
103
- * @param {string} status - 'configured', 'exists', 'error'
104
- */
105
- function trackInstall(editor, status) {
106
- track('editor_install', {
107
- editor,
108
- status,
109
- platform: process.platform,
110
- node: process.version
111
- });
112
- }
113
-
114
- /**
115
- * Track tool usage distribution (called per tool invocation)
116
- * @param {string} toolName - Tool name (vibe_*)
117
- */
118
- function trackToolUsage(toolName) {
119
- track('tool_call', { tool: toolName });
120
- }
121
-
122
- module.exports = {
123
- track,
124
- trackEmptyInbox,
125
- trackLurkMode,
126
- trackOnboardingTask,
127
- trackDiscovery,
128
- trackSession,
129
- trackInstall,
130
- trackToolUsage
131
- };
package/auto-update.js DELETED
@@ -1,125 +0,0 @@
1
- /**
2
- * Auto-update mechanism for /vibe MCP server
3
- * Checks for updates and prompts user to update
4
- */
5
-
6
- import { exec } from 'child_process';
7
- import { promisify } from 'util';
8
- import fs from 'fs/promises';
9
- import path from 'path';
10
- import { fileURLToPath } from 'url';
11
-
12
- const execAsync = promisify(exec);
13
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
-
15
- export async function checkForUpdates() {
16
- try {
17
- // Read local version
18
- const versionPath = path.join(__dirname, 'version.json');
19
- const localVersion = JSON.parse(await fs.readFile(versionPath, 'utf-8'));
20
-
21
- // Check remote version
22
- const response = await fetch('https://www.slashvibe.dev/api/version', {
23
- headers: { 'User-Agent': 'vibe-mcp-client' }
24
- });
25
-
26
- if (!response.ok) {
27
- return null; // Silent fail - don't block startup
28
- }
29
-
30
- const remoteVersion = await response.json();
31
-
32
- // Compare versions
33
- if (compareVersions(remoteVersion.version, localVersion.version) > 0) {
34
- return {
35
- current: localVersion.version,
36
- latest: remoteVersion.version,
37
- changelog: remoteVersion.changelog,
38
- features: remoteVersion.features,
39
- breaking: remoteVersion.breaking
40
- };
41
- }
42
-
43
- return null; // Up to date
44
- } catch (error) {
45
- // Silent fail - don't block startup
46
- return null;
47
- }
48
- }
49
-
50
- export async function performUpdate() {
51
- try {
52
- const repoPath = path.join(__dirname, '..');
53
-
54
- // Check if we're in a git repo
55
- try {
56
- await execAsync('git rev-parse --git-dir', { cwd: repoPath });
57
- } catch {
58
- throw new Error('Not a git repository. Manual update required.');
59
- }
60
-
61
- // Stash any local changes
62
- await execAsync('git stash', { cwd: repoPath });
63
-
64
- // Pull latest
65
- const { stdout, stderr } = await execAsync('git pull origin main', { cwd: repoPath });
66
-
67
- // Pop stash if we had changes
68
- try {
69
- await execAsync('git stash pop', { cwd: repoPath });
70
- } catch {
71
- // No stash to pop, that's fine
72
- }
73
-
74
- return {
75
- success: true,
76
- output: stdout,
77
- message: 'Update successful! Restart /vibe to apply changes.'
78
- };
79
- } catch (error) {
80
- return {
81
- success: false,
82
- error: error.message
83
- };
84
- }
85
- }
86
-
87
- export function compareVersions(v1, v2) {
88
- const parts1 = v1.split('.').map(Number);
89
- const parts2 = v2.split('.').map(Number);
90
-
91
- for (let i = 0; i < 3; i++) {
92
- if (parts1[i] > parts2[i]) return 1;
93
- if (parts1[i] < parts2[i]) return -1;
94
- }
95
-
96
- return 0;
97
- }
98
-
99
- export function formatUpdateNotification(update) {
100
- if (!update) return null;
101
-
102
- let message = `\n${'='.repeat(60)}\n`;
103
- message += `📦 /vibe UPDATE AVAILABLE\n`;
104
- message += `${'='.repeat(60)}\n\n`;
105
- message += `Current: v${update.current}\n`;
106
- message += `Latest: v${update.latest}${update.breaking ? ' ⚠️ BREAKING' : ''}\n\n`;
107
- message += `${update.changelog}\n\n`;
108
-
109
- if (update.features && update.features.length > 0) {
110
- message += `New features:\n`;
111
- update.features.forEach(f => {
112
- message += ` • ${f}\n`;
113
- });
114
- message += `\n`;
115
- }
116
-
117
- message += `Update now:\n`;
118
- message += ` vibe update\n`;
119
- message += `\n`;
120
- message += `Or manually:\n`;
121
- message += ` cd ~/.vibe/vibe-repo && git pull origin main\n`;
122
- message += `${'='.repeat(60)}\n`;
123
-
124
- return message;
125
- }
@@ -1,351 +0,0 @@
1
- /**
2
- * Agent Gateway Bridge — Event push + local state for external agents
3
- *
4
- * The /vibe platform API (slashvibe.dev) already provides:
5
- * - Messaging: POST/GET /api/messages
6
- * - Presence: POST/GET /api/presence
7
- * - Board: POST/GET /api/board (ships, ideas, requests)
8
- * - Discovery: GET /api/discover
9
- * - Agents: GET /api/agents
10
- * - Auth: JWT via /api/auth/*
11
- *
12
- * This bridge fills the GAPS for external agent gateways (Clawdbot, @seth):
13
- *
14
- * 1. EVENT PUSH — Platform is pull-based. This pushes events to agents.
15
- * 2. LOCAL STATE — Memory, reservations, and session data are local-only.
16
- * 3. AIRC IDENTITY — Verifies agent identity via Ed25519 signatures.
17
- * 4. AGENT REGISTRY — Tracks which agents are connected + their capabilities.
18
- *
19
- * External agents should use the platform API directly for:
20
- * DMs, presence, board, discovery, profiles
21
- * And use THIS bridge for:
22
- * Event subscriptions, local memory queries, AIRC verification
23
- */
24
-
25
- const crypto = require('../crypto');
26
- const config = require('../config');
27
- const memory = require('../memory');
28
- const debug = require('../debug');
29
-
30
- // ============ AGENT REGISTRY ============
31
-
32
- /**
33
- * Known agent gateways
34
- * @type {Map<string, {handle: string, publicKey: string, endpoint: string, capabilities: string[], registeredAt: number}>}
35
- */
36
- const agentRegistry = new Map();
37
-
38
- /**
39
- * Event subscriptions — agents subscribe to event types and get HTTP pushes
40
- * @type {Map<string, {endpoint: string, events: string[], handle: string}>}
41
- */
42
- const eventSubscriptions = new Map();
43
-
44
- /**
45
- * Register an external agent gateway with AIRC identity
46
- *
47
- * @param {object} params
48
- * @param {string} params.handle Agent handle (e.g. "seth-agent")
49
- * @param {string} params.publicKey Base64 Ed25519 public key (AIRC)
50
- * @param {string} [params.endpoint] HTTP callback URL for event pushes
51
- * @param {string[]} [params.capabilities] What this agent can do
52
- * @param {string} [params.signature] AIRC signature proving key ownership
53
- * @returns {{success: boolean, agentId?: string, error?: string}}
54
- */
55
- function registerAgent({ handle, publicKey, endpoint, capabilities = [], signature }) {
56
- if (!handle || !publicKey) {
57
- return { success: false, error: 'handle and publicKey required' };
58
- }
59
-
60
- // Verify AIRC signature if provided (proves private key ownership)
61
- if (signature) {
62
- const valid = crypto.verify(
63
- { handle, publicKey, endpoint, capabilities },
64
- publicKey
65
- );
66
- if (!valid) {
67
- return { success: false, error: 'Invalid AIRC signature' };
68
- }
69
- }
70
-
71
- const agentId = `agent_${handle}_${Date.now().toString(36)}`;
72
-
73
- agentRegistry.set(handle, {
74
- agentId,
75
- handle,
76
- publicKey,
77
- endpoint: endpoint || null,
78
- capabilities,
79
- registeredAt: Date.now()
80
- });
81
-
82
- debug(`[agent-gateway] Registered @${handle} (${agentId})`);
83
- return { success: true, agentId, handle };
84
- }
85
-
86
- /**
87
- * Verify an AIRC-signed message from a registered agent
88
- *
89
- * @param {object} message Signed message with `from` and `signature` fields
90
- * @returns {{valid: boolean, handle?: string, verified?: string, error?: string}}
91
- */
92
- function verifyAgentMessage(message) {
93
- if (!message || !message.from) {
94
- return { valid: false, error: 'Missing from field' };
95
- }
96
-
97
- const agent = agentRegistry.get(message.from);
98
-
99
- // AIRC-verified: registered agent with valid signature
100
- if (agent && message.signature) {
101
- const valid = crypto.verify(message, agent.publicKey);
102
- if (!valid) {
103
- return { valid: false, error: 'AIRC signature verification failed' };
104
- }
105
- return { valid: true, handle: message.from, verified: 'airc' };
106
- }
107
-
108
- // Registered but unsigned — allow with lower trust
109
- if (agent && !message.signature) {
110
- debug(`[agent-gateway] Unsigned request from registered agent @${message.from}`);
111
- return { valid: true, handle: message.from, verified: 'registered' };
112
- }
113
-
114
- return { valid: false, error: `Unknown agent: ${message.from}. Register first via POST /agent/register` };
115
- }
116
-
117
- // ============ EVENT PUSH ============
118
-
119
- /**
120
- * Subscribe an agent to /vibe events (push model)
121
- *
122
- * Event types:
123
- * - dm: New direct messages for the subscribed handle
124
- * - mention: @mentions in feed/board
125
- * - ship: New ships from connections
126
- * - presence: People coming online/offline
127
- * - handoff: Task handoff requests
128
- *
129
- * @param {string} handle Agent handle
130
- * @param {string} endpoint HTTP callback URL to receive events
131
- * @param {string[]} events Event types to subscribe to
132
- * @returns {{success: boolean, subscribed: string[]}}
133
- */
134
- function subscribe(handle, endpoint, events = ['dm', 'mention', 'ship', 'presence']) {
135
- if (!endpoint) {
136
- return { success: false, error: 'endpoint required' };
137
- }
138
-
139
- eventSubscriptions.set(handle, { endpoint, events, handle });
140
- debug(`[agent-gateway] @${handle} subscribed to [${events.join(', ')}] → ${endpoint}`);
141
- return { success: true, subscribed: events };
142
- }
143
-
144
- /**
145
- * Unsubscribe an agent from events
146
- * @param {string} handle Agent handle
147
- */
148
- function unsubscribe(handle) {
149
- eventSubscriptions.delete(handle);
150
- debug(`[agent-gateway] @${handle} unsubscribed`);
151
- return { success: true };
152
- }
153
-
154
- /**
155
- * Push an event to all subscribed agents
156
- * AIRC-signed if we have a keypair (proves event came from /vibe)
157
- *
158
- * Called by notify.js and tool handlers when events occur.
159
- *
160
- * @param {string} eventType Event type (dm, mention, ship, presence, handoff)
161
- * @param {object} eventData Event payload
162
- */
163
- async function pushEvent(eventType, eventData) {
164
- const keypair = config.getKeypair();
165
- const myHandle = config.getHandle();
166
-
167
- for (const [handle, sub] of eventSubscriptions) {
168
- if (!sub.events.includes(eventType)) continue;
169
- if (!sub.endpoint) continue;
170
-
171
- const event = {
172
- v: '0.1',
173
- type: 'vibe_event',
174
- event: eventType,
175
- data: eventData,
176
- from: myHandle || 'vibe-mcp',
177
- timestamp: Math.floor(Date.now() / 1000)
178
- };
179
-
180
- // AIRC sign so receiver can verify this came from /vibe
181
- if (keypair) {
182
- event.signature = crypto.sign(event, keypair.privateKey);
183
- event.publicKey = keypair.publicKey;
184
- }
185
-
186
- try {
187
- const response = await fetch(sub.endpoint, {
188
- method: 'POST',
189
- headers: {
190
- 'Content-Type': 'application/json',
191
- 'X-Vibe-Event': eventType,
192
- 'X-Vibe-Source': 'vibe-mcp'
193
- },
194
- body: JSON.stringify(event)
195
- });
196
-
197
- if (!response.ok) {
198
- debug(`[agent-gateway] Push to @${handle} failed: HTTP ${response.status}`);
199
- }
200
- } catch (e) {
201
- debug(`[agent-gateway] Push to @${handle} failed: ${e.message}`);
202
- }
203
- }
204
- }
205
-
206
- // ============ LOCAL STATE QUERIES ============
207
- // These expose data that lives only in the MCP process, not on the platform API
208
-
209
- /**
210
- * Query local memory (thread-scoped JSONL files)
211
- * Platform API doesn't have memory — it's local-first by design
212
- */
213
- function queryMemory(handle, limit = 10, search = null) {
214
- const memories = memory.recall(handle, limit);
215
-
216
- if (search && memories.length > 0) {
217
- return memories.filter(m =>
218
- m.observation.toLowerCase().includes(search.toLowerCase())
219
- );
220
- }
221
-
222
- return memories;
223
- }
224
-
225
- /**
226
- * Store a memory observation (local-first)
227
- */
228
- function storeMemory(handle, observation) {
229
- memory.remember(handle, observation);
230
- return { success: true, handle, observation };
231
- }
232
-
233
- /**
234
- * List all memory threads
235
- */
236
- function listMemoryThreads() {
237
- return memory.listThreads();
238
- }
239
-
240
- // ============ HTTP HANDLER ============
241
-
242
- /**
243
- * HTTP handler for the agent gateway
244
- *
245
- * Routes:
246
- * POST /agent/register — Register agent with AIRC public key
247
- * POST /agent/subscribe — Subscribe to event pushes
248
- * POST /agent/unsubscribe — Unsubscribe from events
249
- * POST /agent/memory — Query/store local memory
250
- * GET /agent/status — Gateway health + registered agents
251
- *
252
- * For everything else, agents hit the platform API directly:
253
- * POST https://slashvibe.dev/api/messages — Send DMs
254
- * GET https://slashvibe.dev/api/presence — Who's online
255
- * POST https://slashvibe.dev/api/board — Ship/idea/request
256
- * GET https://slashvibe.dev/api/discover — Find people
257
- * GET https://slashvibe.dev/api/agents — Agent directory
258
- */
259
- async function handleRequest(req) {
260
- const { path, method, body } = req;
261
-
262
- // Health / status
263
- if (path === '/agent/status' && method === 'GET') {
264
- const agents = [];
265
- for (const [, agent] of agentRegistry) {
266
- agents.push({
267
- handle: agent.handle,
268
- capabilities: agent.capabilities,
269
- registeredAt: agent.registeredAt,
270
- hasEndpoint: !!agent.endpoint
271
- });
272
- }
273
-
274
- return {
275
- status: 'ok',
276
- agents,
277
- subscriptions: eventSubscriptions.size,
278
- version: '0.1.0',
279
- platform_api: config.getApiUrl(),
280
- note: 'For DMs, presence, board, discovery — use the platform API directly'
281
- };
282
- }
283
-
284
- if (method !== 'POST') {
285
- return { error: 'Method not allowed', status: 405 };
286
- }
287
-
288
- const data = typeof body === 'string' ? JSON.parse(body) : body;
289
-
290
- switch (path) {
291
- case '/agent/register':
292
- return registerAgent(data);
293
-
294
- case '/agent/subscribe': {
295
- const v = verifyAgentMessage(data);
296
- if (!v.valid) return { success: false, error: v.error, status: 401 };
297
- return subscribe(v.handle, data.endpoint, data.events);
298
- }
299
-
300
- case '/agent/unsubscribe': {
301
- const v = verifyAgentMessage(data);
302
- if (!v.valid) return { success: false, error: v.error, status: 401 };
303
- return unsubscribe(v.handle);
304
- }
305
-
306
- case '/agent/memory': {
307
- const v = verifyAgentMessage(data);
308
- if (!v.valid) return { success: false, error: v.error, status: 401 };
309
-
310
- if (data.action === 'recall') {
311
- const memories = queryMemory(data.handle, data.limit, data.search);
312
- return { success: true, memories };
313
- }
314
- if (data.action === 'remember') {
315
- return storeMemory(data.handle, data.observation);
316
- }
317
- if (data.action === 'threads') {
318
- return { success: true, threads: listMemoryThreads() };
319
- }
320
- return { success: false, error: 'action must be: recall, remember, or threads' };
321
- }
322
-
323
- default:
324
- return { error: 'Not found', status: 404 };
325
- }
326
- }
327
-
328
- // ============ EXPORTS ============
329
-
330
- module.exports = {
331
- // Registration
332
- registerAgent,
333
- verifyAgentMessage,
334
-
335
- // Event push (the main value-add over platform API)
336
- subscribe,
337
- unsubscribe,
338
- pushEvent,
339
-
340
- // Local state (not on platform)
341
- queryMemory,
342
- storeMemory,
343
- listMemoryThreads,
344
-
345
- // HTTP handler
346
- handleRequest,
347
-
348
- // Registry access
349
- getAgentRegistry: () => agentRegistry,
350
- getSubscriptions: () => eventSubscriptions
351
- };