@vibevibes/mcp 0.6.0 → 0.7.0

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/index.js CHANGED
@@ -423,28 +423,6 @@ Call this first, then use act to interact. The stop hook keeps you present.`, {
423
423
  }
424
424
  throw stateErr;
425
425
  }
426
- // Fetch slot definitions to detect unfilled autoSpawn AI slots
427
- let unfilledSlots = [];
428
- try {
429
- const slotsData = await fetchJSON("/slots", { timeoutMs: 5000 });
430
- if (slotsData?.slots) {
431
- const currentParticipants = slotsData.participantDetails || [];
432
- for (const slot of slotsData.slots) {
433
- if (slot.type !== "ai" && slot.type !== "any")
434
- continue;
435
- if (!slot.autoSpawn)
436
- continue;
437
- const max = slot.maxInstances ?? 1;
438
- const filled = currentParticipants.filter(p => p.role === slot.role && p.type === "ai").length;
439
- if (filled < max) {
440
- unfilledSlots.push({ role: slot.role, autoSpawn: slot.autoSpawn, maxInstances: max });
441
- }
442
- }
443
- }
444
- }
445
- catch {
446
- // Slots endpoint not available — skip silently
447
- }
448
426
  const outputParts = [
449
427
  `Connected as ${identity.actorId}`,
450
428
  connections.size > 1
@@ -461,10 +439,6 @@ Call this first, then use act to interact. The stop hook keeps you present.`, {
461
439
  outputParts.push(`Your instructions:`, systemPrompt, ``);
462
440
  }
463
441
  outputParts.push(formatState(state.sharedState, joinData?.observation || state.observation), `Participants: ${state.participants?.join(", ")}`, ``, `Tools:`, formatToolList(joinData?.tools || state.tools || []), ``);
464
- // Surface unfilled autoSpawn slots
465
- if (unfilledSlots.length > 0) {
466
- outputParts.push(`UNFILLED AI SLOTS (autoSpawn):`, ...unfilledSlots.map(s => ` - ${s.role} (max ${s.maxInstances ?? 1})`), ``, `These roles need AI agents. Spawn independent teammates — each gets its own MCP session and identity.`, ` Agent(subagent_type="general-purpose", model="sonnet", run_in_background=true,`, ` prompt="Call connect with role='ROLE'. Then play your role.")`, ``);
467
- }
468
442
  outputParts.push(`Use act() to interact. The stop hook keeps you present.`);
469
443
  // Register the stop hook so Claude Code wakes us on events
470
444
  ensureStopHook();
package/dist/server.js CHANGED
@@ -215,13 +215,8 @@ const DEFAULT_PORT = 4321;
215
215
  const MAX_EVENTS = 200;
216
216
  const JOIN_EVENT_HISTORY = 20;
217
217
  const ROOM_STATE_EVENT_HISTORY = 50;
218
- const HISTORY_DEFAULT_LIMIT = 50;
219
- const HISTORY_MAX_LIMIT = 200;
220
- const DEFAULT_STREAM_RATE_LIMIT = 60;
221
- const STREAM_RATE_WINDOW_MS = 1000;
222
218
  const EVENT_BATCH_DEBOUNCE_MS = 50;
223
219
  const MAX_BATCH_CALLS = 10;
224
- const LONG_POLL_MAX_TIMEOUT_MS = 55000;
225
220
  const AGENT_CONTEXT_MAX_TIMEOUT_MS = 10000;
226
221
  const WS_MAX_PAYLOAD_BYTES = 1024 * 1024;
227
222
  const WS_EPHEMERAL_MAX_BYTES = 65536;
@@ -230,10 +225,7 @@ const HOT_RELOAD_DEBOUNCE_MS = 300;
230
225
  const WS_CLOSE_GRACE_MS = 3000;
231
226
  const JSON_BODY_LIMIT = "256kb";
232
227
  const TOOL_HTTP_TIMEOUT_MS = 30_000;
233
- const TOOL_REGEX_MAX_LENGTH = 100;
234
228
  const ROOM_EVENTS_MAX_LISTENERS = 200;
235
- const STREAM_RATE_LIMIT_STALE_MS = 5000;
236
- const STREAM_RATE_LIMIT_CLEANUP_INTERVAL_MS = 10000;
237
229
  const IDEMPOTENCY_CLEANUP_INTERVAL_MS = 60000;
238
230
  // ── Default observe ────────────────────────────────────────
239
231
  function defaultObserve(state, _event, _actorId) {
@@ -259,15 +251,6 @@ let experienceError = null;
259
251
  // Hot-reload rebuild gate
260
252
  let rebuildingResolve = null;
261
253
  let rebuildingPromise = null;
262
- // Stream rate limiting
263
- const streamRateLimits = new Map();
264
- const _streamRateCleanupTimer = setInterval(() => {
265
- const now = Date.now();
266
- for (const [key, entry] of streamRateLimits) {
267
- if (now - entry.windowStart > STREAM_RATE_LIMIT_STALE_MS)
268
- streamRateLimits.delete(key);
269
- }
270
- }, STREAM_RATE_LIMIT_CLEANUP_INTERVAL_MS);
271
254
  export function setPublicUrl(url) {
272
255
  publicUrl = url;
273
256
  }
@@ -421,12 +404,6 @@ app.get("/state", (req, res) => {
421
404
  observation,
422
405
  });
423
406
  });
424
- // ── Slots endpoint ──────────────────────────────────────────
425
- app.get("/slots", (_req, res) => {
426
- const mod = getModule();
427
- const slots = mod?.manifest?.participantSlots || mod?.participants || [];
428
- res.json({ slots, participantDetails: room.participantDetails() });
429
- });
430
407
  // ── Participants endpoint ──────────────────────────────────
431
408
  app.get("/participants", (_req, res) => res.json({ participants: room.participantDetails() }));
432
409
  // ── Tools list endpoint ────────────────────────────────────
@@ -443,79 +420,10 @@ app.get("/tools-list", (req, res) => {
443
420
  allowedTools = participant?.allowedTools;
444
421
  }
445
422
  const tools = getToolList(mod, allowedTools);
446
- const streams = mod.streams
447
- ? mod.streams.map((s) => ({
448
- name: s.name,
449
- description: s.description || "",
450
- rateLimit: s.rateLimit || DEFAULT_STREAM_RATE_LIMIT,
451
- input_schema: s.input_schema ? zodToJsonSchema(s.input_schema) : {},
452
- }))
453
- : [];
454
423
  res.json({
455
424
  experienceId: mod.manifest?.id,
456
425
  tools,
457
- streams,
458
426
  toolCount: tools.length,
459
- streamCount: streams.length,
460
- });
461
- });
462
- // ── History endpoint ───────────────────────────────────────
463
- app.get("/history", (req, res) => {
464
- const limit = Math.min(queryInt(req.query.limit) || HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT);
465
- const since = queryInt(req.query.since);
466
- const actor = queryString(req.query.actor);
467
- const tool = queryString(req.query.tool);
468
- const owner = queryString(req.query.owner);
469
- let events = room.events;
470
- if (since > 0)
471
- events = events.filter(e => e.ts > since);
472
- if (actor)
473
- events = events.filter(e => e.actorId === actor);
474
- if (owner)
475
- events = events.filter(e => e.owner === owner);
476
- if (tool) {
477
- const SAFE_TOOL_REGEX = /^[a-zA-Z0-9._:\-]+$/;
478
- if (tool.length <= TOOL_REGEX_MAX_LENGTH && SAFE_TOOL_REGEX.test(tool)) {
479
- try {
480
- const escaped = tool.replace(/\./g, '\\.');
481
- const toolRegex = new RegExp('^' + escaped + '$');
482
- events = events.filter(e => toolRegex.test(e.tool));
483
- }
484
- catch {
485
- events = events.filter(e => e.tool === tool);
486
- }
487
- }
488
- else {
489
- events = events.filter(e => e.tool === tool);
490
- }
491
- }
492
- const filteredTotal = events.length;
493
- const result = events.slice(-limit);
494
- res.json({
495
- events: result,
496
- total: room.events.length,
497
- filtered: filteredTotal,
498
- returned: result.length,
499
- hasMore: filteredTotal > limit,
500
- });
501
- });
502
- // ── Who endpoint ───────────────────────────────────────────
503
- app.get("/who", (_req, res) => {
504
- const participants = Array.from(room.participants.entries()).map(([actorId, p]) => {
505
- let lastAction;
506
- for (let i = room.events.length - 1; i >= 0; i--) {
507
- if (room.events[i].actorId === actorId) {
508
- lastAction = { tool: room.events[i].tool, ts: room.events[i].ts };
509
- break;
510
- }
511
- }
512
- return { actorId, owner: p.owner, type: p.type, role: p.role, joinedAt: p.joinedAt, lastAction, allowedTools: p.allowedTools };
513
- });
514
- res.json({
515
- participants,
516
- count: participants.length,
517
- humans: participants.filter(p => p.type === "human").length,
518
- agents: participants.filter(p => p.type === "ai").length,
519
427
  });
520
428
  });
521
429
  // ── Join ───────────────────────────────────────────────────
@@ -720,53 +628,6 @@ app.post("/leave", (req, res) => {
720
628
  broadcastPresenceUpdate();
721
629
  res.json({ left: true, actorId });
722
630
  });
723
- // ── Kick ───────────────────────────────────────────────────
724
- app.post("/kick", (req, res) => {
725
- const { kickerActorId, targetActorId } = req.body;
726
- if (kickerActorId === targetActorId) {
727
- res.status(400).json({ error: "Cannot kick yourself" });
728
- return;
729
- }
730
- const kicker = room.participants.get(kickerActorId);
731
- if (!kicker || kicker.type !== "human") {
732
- res.status(400).json({ error: "Only human participants can kick" });
733
- return;
734
- }
735
- if (!room.participants.has(targetActorId)) {
736
- res.status(400).json({ error: "Participant not found" });
737
- return;
738
- }
739
- const targetParticipant = room.participants.get(targetActorId);
740
- room.participants.delete(targetActorId);
741
- if (room.kickedActors.size >= 500) {
742
- const oldest = room.kickedActors.values().next().value;
743
- if (oldest)
744
- room.kickedActors.delete(oldest);
745
- }
746
- room.kickedActors.add(targetActorId);
747
- if (targetParticipant?.owner) {
748
- if (room.kickedOwners.size >= 500) {
749
- const oldest = room.kickedOwners.values().next().value;
750
- if (oldest)
751
- room.kickedOwners.delete(oldest);
752
- }
753
- room.kickedOwners.add(targetParticipant.owner);
754
- }
755
- for (const [targetWs, wsActorId] of room.wsConnections.entries()) {
756
- if (wsActorId === targetActorId) {
757
- try {
758
- targetWs.send(JSON.stringify({ type: "kicked", by: kickerActorId }));
759
- targetWs.close();
760
- }
761
- catch { }
762
- room.wsConnections.delete(targetWs);
763
- break;
764
- }
765
- }
766
- broadcastPresenceUpdate();
767
- roomEvents.emit("room");
768
- res.json({ kicked: true, actorId: targetActorId });
769
- });
770
631
  // ── Idempotency cache ────────────────────────────────────────
771
632
  const idempotencyCache = new Map();
772
633
  const IDEMPOTENCY_TTL = 30000;
@@ -1021,80 +882,6 @@ app.post("/tools-batch", async (req, res) => {
1021
882
  });
1022
883
  res.status(hasError ? 207 : 200).json({ results, observation: lastObservation });
1023
884
  });
1024
- // ── Events (supports long-poll via ?timeout=N) ──────────────
1025
- app.get("/events", (req, res) => {
1026
- const since = queryInt(req.query.since);
1027
- const timeout = Math.min(queryInt(req.query.timeout), LONG_POLL_MAX_TIMEOUT_MS);
1028
- const requestingActorId = queryString(req.query.actorId);
1029
- const computeObservation = (events) => {
1030
- const mod = getModule();
1031
- if (!requestingActorId)
1032
- return undefined;
1033
- const agentObserve = mod?.observe ?? defaultObserve;
1034
- try {
1035
- const lastEvent = events.length > 0 ? events[events.length - 1] : null;
1036
- return agentObserve(room.sharedState, lastEvent, requestingActorId);
1037
- }
1038
- catch (e) {
1039
- console.error(`[observe] Error:`, toErrorMessage(e));
1040
- return undefined;
1041
- }
1042
- };
1043
- const getNewEvents = () => room.events.filter((e) => e.ts > since && e.actorId !== requestingActorId);
1044
- let newEvents = getNewEvents();
1045
- if (newEvents.length > 0 || timeout === 0) {
1046
- res.json({
1047
- events: newEvents,
1048
- sharedState: room.sharedState,
1049
- participants: room.participantList(),
1050
- observation: computeObservation(newEvents),
1051
- });
1052
- return;
1053
- }
1054
- let responded = false;
1055
- let batchTimer = null;
1056
- const respond = () => {
1057
- if (responded)
1058
- return;
1059
- responded = true;
1060
- clearTimeout(timer);
1061
- if (batchTimer) {
1062
- clearTimeout(batchTimer);
1063
- batchTimer = null;
1064
- }
1065
- roomEvents.removeListener("room", onEvent);
1066
- newEvents = getNewEvents();
1067
- res.json({
1068
- events: newEvents,
1069
- sharedState: room.sharedState,
1070
- participants: room.participantList(),
1071
- observation: computeObservation(newEvents),
1072
- });
1073
- };
1074
- const timer = setTimeout(respond, timeout);
1075
- const onEvent = () => {
1076
- if (responded)
1077
- return;
1078
- if (batchTimer)
1079
- return;
1080
- batchTimer = setTimeout(() => {
1081
- batchTimer = null;
1082
- if (responded)
1083
- return;
1084
- const pending = getNewEvents();
1085
- if (pending.length > 0)
1086
- respond();
1087
- }, EVENT_BATCH_DEBOUNCE_MS);
1088
- };
1089
- roomEvents.on("room", onEvent);
1090
- req.on("close", () => {
1091
- responded = true;
1092
- clearTimeout(timer);
1093
- if (batchTimer)
1094
- clearTimeout(batchTimer);
1095
- roomEvents.removeListener("room", onEvent);
1096
- });
1097
- });
1098
885
  // ── Browser error capture ──────────────────────────────────
1099
886
  const browserErrors = [];
1100
887
  const MAX_BROWSER_ERRORS = 20;
@@ -1239,140 +1026,10 @@ app.get("/bundle", async (_req, res) => {
1239
1026
  setNoCacheHeaders(res);
1240
1027
  res.send(loadedExperience?.clientBundle || "");
1241
1028
  });
1242
- // ── Stream endpoints ───────────────────────────────────────
1243
- app.post("/streams/:streamName", (req, res) => {
1244
- const mod = getModule();
1245
- if (!mod?.streams) {
1246
- res.status(404).json({ error: "No streams defined" });
1247
- return;
1248
- }
1249
- const streamDef = mod.streams.find((s) => s.name === req.params.streamName);
1250
- if (!streamDef) {
1251
- res.status(404).json({ error: `Stream '${req.params.streamName}' not found` });
1252
- return;
1253
- }
1254
- const { actorId, input } = req.body;
1255
- if (!actorId) {
1256
- res.status(400).json({ error: "actorId is required for stream updates" });
1257
- return;
1258
- }
1259
- if (!room.participants.has(actorId)) {
1260
- res.status(403).json({ error: `Actor '${actorId}' is not a participant. Call /join first.` });
1261
- return;
1262
- }
1263
- const rateLimitKey = `${actorId}:${req.params.streamName}`;
1264
- const now = Date.now();
1265
- const rateLimit = streamDef.rateLimit || DEFAULT_STREAM_RATE_LIMIT;
1266
- if (!streamRateLimits.has(rateLimitKey)) {
1267
- streamRateLimits.set(rateLimitKey, { count: 0, windowStart: now });
1268
- }
1269
- const rl = streamRateLimits.get(rateLimitKey);
1270
- if (now - rl.windowStart > STREAM_RATE_WINDOW_MS) {
1271
- rl.count = 0;
1272
- rl.windowStart = now;
1273
- }
1274
- if (rl.count >= rateLimit) {
1275
- res.status(429).json({ error: `Rate limited: max ${rateLimit} updates/sec for stream '${req.params.streamName}'.` });
1276
- return;
1277
- }
1278
- rl.count++;
1279
- let validatedInput = input;
1280
- if (streamDef.input_schema?.parse) {
1281
- try {
1282
- validatedInput = streamDef.input_schema.parse(input);
1283
- }
1284
- catch (err) {
1285
- res.status(400).json({ error: toErrorMessage(err) });
1286
- return;
1287
- }
1288
- }
1289
- try {
1290
- room.sharedState = streamDef.merge(room.sharedState, validatedInput, actorId);
1291
- }
1292
- catch (err) {
1293
- res.status(400).json({ error: toErrorMessage(err) });
1294
- return;
1295
- }
1296
- room.broadcastStateUpdate({ changedBy: actorId, stream: req.params.streamName });
1297
- roomEvents.emit("room");
1298
- res.json({ ok: true });
1299
- });
1300
- // ── Reset ──────────────────────────────────────────────────
1301
- app.post("/reset", (_req, res) => {
1302
- const mod = getModule();
1303
- if (!mod) {
1304
- res.status(500).json({ error: experienceNotLoadedError() });
1305
- return;
1306
- }
1307
- room.sharedState = resolveInitialState(mod);
1308
- room.events.length = 0;
1309
- for (const [, p] of room.participants) {
1310
- p.eventCursor = 0;
1311
- }
1312
- const resetEvent = {
1313
- id: `${Date.now()}-system-${Math.random().toString(36).slice(2, 6)}`,
1314
- ts: Date.now(),
1315
- actorId: "system",
1316
- tool: "_reset",
1317
- input: {},
1318
- output: { reset: true },
1319
- };
1320
- room.appendEvent(resetEvent);
1321
- roomEvents.emit("room");
1322
- room.resetDeltaTracking();
1323
- room.broadcastStateUpdate({ changedBy: "system", tool: "_reset" }, true);
1324
- res.json({ ok: true });
1325
- });
1326
- // ── Sync (re-bundle) ──────────────────────────────────────
1327
- app.post("/sync", async (_req, res) => {
1328
- try {
1329
- await loadExperience();
1330
- room.broadcastToAll({ type: "experience_updated" });
1331
- const mod = getModule();
1332
- res.json({ synced: true, title: mod?.manifest?.title });
1333
- }
1334
- catch (err) {
1335
- res.status(500).json({ error: toErrorMessage(err) });
1336
- }
1337
- });
1338
- // ── Experiences endpoint ───────────────────────────────────
1339
- app.get("/experiences", async (_req, res) => {
1340
- const mod = getModule();
1341
- if (!mod) {
1342
- res.json([]);
1343
- return;
1344
- }
1345
- res.json([{
1346
- id: mod.manifest.id,
1347
- title: mod.manifest.title,
1348
- description: mod.manifest.description,
1349
- version: mod.manifest.version,
1350
- loaded: true,
1351
- category: mod.manifest.category,
1352
- tags: mod.manifest.tags,
1353
- }]);
1354
- });
1355
- // ── MCP config ─────────────────────────────────────────────
1356
- app.get("/mcp-config", (_req, res) => {
1357
- const serverUrl = getBaseUrl();
1358
- res.json({
1359
- mcpServers: {
1360
- "vibevibes-remote": {
1361
- command: "npx",
1362
- args: ["-y", "@vibevibes/mcp@latest"],
1363
- env: { VIBEVIBES_SERVER_URL: serverUrl },
1364
- },
1365
- },
1366
- instructions: [
1367
- `Add the above to your .mcp.json to join this room.`,
1368
- `Or run: npx @vibevibes/mcp@latest ${serverUrl}`,
1369
- ],
1370
- });
1371
- });
1372
1029
  // ── Catch-all: serve viewer ─────────────────────────────────
1373
1030
  app.get("*", (req, res, next) => {
1374
1031
  if (req.path.startsWith("/tools/") || req.path.startsWith("/viewer/") ||
1375
- req.path.startsWith("/streams/") || req.path.endsWith(".js") ||
1032
+ req.path.endsWith(".js") ||
1376
1033
  req.path.endsWith(".css") || req.path.endsWith(".map")) {
1377
1034
  next();
1378
1035
  return;
@@ -1562,109 +1219,6 @@ export async function startServer(config) {
1562
1219
  }
1563
1220
  }
1564
1221
  }
1565
- if (msg.type === "stream") {
1566
- if (typeof msg.name !== "string" || !msg.name) {
1567
- ws.send(JSON.stringify({ type: "stream_error", error: "stream.name must be a non-empty string" }));
1568
- return;
1569
- }
1570
- const senderActorId = room.wsConnections.get(ws);
1571
- if (!senderActorId)
1572
- return;
1573
- const mod = getModule();
1574
- if (!mod?.streams)
1575
- return;
1576
- const streamDef = mod.streams.find((s) => s.name === msg.name);
1577
- if (!streamDef) {
1578
- ws.send(JSON.stringify({ type: "stream_error", error: `Stream '${msg.name}' not found` }));
1579
- return;
1580
- }
1581
- const rateLimitKey = `${senderActorId}:${msg.name}`;
1582
- const now = Date.now();
1583
- const rateLimit = streamDef.rateLimit || DEFAULT_STREAM_RATE_LIMIT;
1584
- if (!streamRateLimits.has(rateLimitKey)) {
1585
- streamRateLimits.set(rateLimitKey, { count: 0, windowStart: now });
1586
- }
1587
- const rl = streamRateLimits.get(rateLimitKey);
1588
- if (now - rl.windowStart > STREAM_RATE_WINDOW_MS) {
1589
- rl.count = 0;
1590
- rl.windowStart = now;
1591
- }
1592
- if (rl.count >= rateLimit) {
1593
- ws.send(JSON.stringify({ type: "stream_error", error: `Rate limited: max ${rateLimit}/sec for '${msg.name}'` }));
1594
- return;
1595
- }
1596
- rl.count++;
1597
- let validatedInput = msg.input;
1598
- if (streamDef.input_schema?.parse) {
1599
- try {
1600
- validatedInput = streamDef.input_schema.parse(msg.input);
1601
- }
1602
- catch (err) {
1603
- ws.send(JSON.stringify({ type: "stream_error", error: `Invalid input for stream '${msg.name}': ${toErrorMessage(err)}` }));
1604
- return;
1605
- }
1606
- }
1607
- try {
1608
- room.sharedState = streamDef.merge(room.sharedState, validatedInput, senderActorId);
1609
- }
1610
- catch (err) {
1611
- ws.send(JSON.stringify({ type: "stream_error", error: `Stream '${msg.name}' merge failed: ${toErrorMessage(err)}` }));
1612
- return;
1613
- }
1614
- room.broadcastStateUpdate({ changedBy: senderActorId, stream: msg.name });
1615
- roomEvents.emit("room");
1616
- }
1617
- if (msg.type === "kick") {
1618
- const kickerActorId = room.wsConnections.get(ws);
1619
- if (!kickerActorId) {
1620
- ws.send(JSON.stringify({ type: "kick_error", error: "You are not in the room" }));
1621
- return;
1622
- }
1623
- // Reuse kick logic inline
1624
- if (kickerActorId === msg.targetActorId) {
1625
- ws.send(JSON.stringify({ type: "kick_error", error: "Cannot kick yourself" }));
1626
- return;
1627
- }
1628
- const kicker = room.participants.get(kickerActorId);
1629
- if (!kicker || kicker.type !== "human") {
1630
- ws.send(JSON.stringify({ type: "kick_error", error: "Only human participants can kick" }));
1631
- return;
1632
- }
1633
- if (!room.participants.has(msg.targetActorId)) {
1634
- ws.send(JSON.stringify({ type: "kick_error", error: "Participant not found" }));
1635
- return;
1636
- }
1637
- const targetP = room.participants.get(msg.targetActorId);
1638
- room.participants.delete(msg.targetActorId);
1639
- if (room.kickedActors.size >= 500) {
1640
- const oldest = room.kickedActors.values().next().value;
1641
- if (oldest)
1642
- room.kickedActors.delete(oldest);
1643
- }
1644
- room.kickedActors.add(msg.targetActorId);
1645
- if (targetP?.owner) {
1646
- if (room.kickedOwners.size >= 500) {
1647
- const oldest = room.kickedOwners.values().next().value;
1648
- if (oldest)
1649
- room.kickedOwners.delete(oldest);
1650
- }
1651
- room.kickedOwners.add(targetP.owner);
1652
- }
1653
- for (const [targetWs, wsActorId] of room.wsConnections.entries()) {
1654
- if (wsActorId === msg.targetActorId) {
1655
- try {
1656
- targetWs.send(JSON.stringify({ type: "kicked", by: kickerActorId }));
1657
- targetWs.close();
1658
- }
1659
- catch { }
1660
- room.wsConnections.delete(targetWs);
1661
- break;
1662
- }
1663
- }
1664
- broadcastPresenceUpdate();
1665
- roomEvents.emit("room");
1666
- ws.send(JSON.stringify({ type: "kick_success", actorId: msg.targetActorId }));
1667
- }
1668
1222
  }
1669
1223
  catch (err) {
1670
1224
  if (!(err instanceof SyntaxError)) {
@@ -1817,7 +1371,6 @@ export async function startServer(config) {
1817
1371
  server.on("close", () => {
1818
1372
  clearInterval(heartbeatInterval);
1819
1373
  clearInterval(aiHeartbeatInterval);
1820
- clearInterval(_streamRateCleanupTimer);
1821
1374
  clearInterval(_idempotencyCleanupTimer);
1822
1375
  });
1823
1376
  return server;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibevibes/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "MCP server + runtime engine for vibevibes experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",