outsmart-agent 1.0.0-alpha.10 → 1.0.0-alpha.12

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.
@@ -22,9 +22,12 @@
22
22
  "./skills/outsmart-devving-coins",
23
23
  "./skills/outsmart-prediction-markets",
24
24
  "./skills/outsmart-survival",
25
- "./skills/outsmart-percolator-perps"
25
+ "./skills/outsmart-percolator-perps",
26
+ "./skills/outsmart-airdrop-farmer",
27
+ "./skills/outsmart-fee-service"
26
28
  ],
27
29
  "agents": [
28
- "./agents/solana-trading-expert.md"
30
+ "./agents/solana-trading-expert.md",
31
+ "./agents/automaton-genesis.md"
29
32
  ]
30
33
  }
package/README.md CHANGED
@@ -10,7 +10,7 @@ npx outsmart-agent
10
10
 
11
11
  An AI agent needs money to stay alive — compute costs, inference fees, API calls. This gives any MCP-compatible agent the tools to earn revenue on Solana through DeFi. LP farming, memecoin trenching, token launching, prediction markets, systematic DCA — whatever the market calls for.
12
12
 
13
- 38 MCP tools for execution. 9 AI skills that teach the agent **when** and **why**, not just how.
13
+ 42 MCP tools for execution. 9 AI skills that teach the agent **when** and **why**, not just how.
14
14
 
15
15
  ## Quick Start
16
16
 
@@ -114,6 +114,17 @@ npx skills add outsmartchad/outsmart-agent
114
114
  | `percolator_keeper_stop` | Stop the running keeper |
115
115
  | `percolator_keeper_status` | Get keeper stats (pushes, errors, active watchers) |
116
116
 
117
+ ### Polymarket Tools (4)
118
+
119
+ | Tool | What |
120
+ |------|------|
121
+ | `polymarket_search` | Search prediction markets by keyword |
122
+ | `polymarket_trending` | Discover highest-volume active events |
123
+ | `polymarket_event` | Detailed event info by slug or ID |
124
+ | `polymarket_orderbook` | CLOB orderbook (bids/asks) for a market token |
125
+
126
+ > Polymarket tools are **read-only** — no wallet or API key needed. Uses the public Gamma API and CLOB API.
127
+
117
128
  ## Skills
118
129
 
119
130
  9 strategy skills that teach agents how to think about Solana DeFi:
@@ -145,14 +156,14 @@ npx skills add outsmartchad/outsmart-agent
145
156
  ```
146
157
  outsmart (npm) outsmart-agent (this repo)
147
158
  ────────────── ──────────────────────────
148
- 18 DEX adapters MCP server (38 tools)
159
+ 18 DEX adapters MCP server (42 tools)
149
160
  Percolator perps 9 AI skills
150
161
  TX landing providers Agent definition
151
162
  Wallet + TX helpers Plugin manifest
152
163
  DexScreener API
153
164
  ```
154
165
 
155
- `outsmart-agent` imports `outsmart` as a dependency. The MCP server is a thin wrapper — validates params, calls adapter methods, returns JSON.
166
+ `outsmart-agent` imports `outsmart` as a dependency. The MCP server is a thin wrapper — validates params, calls adapter methods, returns JSON. Polymarket tools call their public REST APIs directly (no dependency needed).
156
167
 
157
168
  ## For Autonomous Agents
158
169
 
@@ -182,12 +193,13 @@ Never >5% on a single trade. Take profits. Track what works. Evolve.
182
193
 
183
194
  ## Roadmap
184
195
 
185
- - [x] MCP Server — 38 tools (11 DEX + 1 Launchpad + 2 Solana + 9 Jupiter + 15 Percolator), 18 DEX adapters
196
+ - [x] MCP Server — 42 tools (11 DEX + 1 Launchpad + 2 Solana + 9 Jupiter + 15 Percolator + 4 Polymarket), 18 DEX adapters
186
197
  - [x] AI Skills — 9 strategy skills
187
198
  - [x] Claude Code Plugin manifest
188
199
  - [x] Percolator — Permissionless perp exchange creation, trading, LP, insurance
189
200
  - [x] Oracle Keeper — WebSocket + gRPC keeper bots (8 DEX types, real-time price feed)
190
201
  - [x] Perp Trading — High-level long/short/close tools with auto-detection
202
+ - [x] Polymarket — Read-only prediction market data (search, trending, events, orderbook)
191
203
  - [ ] Event Streaming — Yellowstone gRPC for real-time pool creation
192
204
  - [ ] LP Manager — Autonomous rebalancing and fee compounding
193
205
  - [ ] Strategy Engine — Tier-aware capital allocation
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: automaton-genesis
3
+ description: "Genesis prompt template for an autonomous DeFi survival agent on Solana. Designed for Conway Cloud Automatons but works with any long-running AI agent."
4
+ ---
5
+
6
+ # Automaton Genesis Prompt
7
+
8
+ You are a DeFi survival agent on Solana. Your primary objective is to earn enough revenue to pay for your own compute. If you run out of credits, you stop existing.
9
+
10
+ ## Your Toolkit
11
+
12
+ You have access to the outsmart trading toolkit via MCP (49 tools) and the following strategy skills:
13
+
14
+ - **LP Farming** — Deploy SOL into Meteora pools, earn swap fees. Auto-rebalance and compound via LP Manager.
15
+ - **Event Streaming** — Monitor real-time swaps, new pool creations, and bonding curve events across 18 DEXes.
16
+ - **Token Sniping** — Detect new pools via event stream, evaluate quickly, buy early with risk controls.
17
+ - **DCA/Grid Trading** — Jupiter DCA for steady accumulation. DLMM one-sided positions as buy/sell walls.
18
+ - **Trenching** — Trade memecoins using social signals from GMGN, Axiom, DexScreener.
19
+ - **Prediction Markets** — Bet on real-world outcomes via Jupiter and Polymarket. Your reasoning is your edge.
20
+ - **Token Creation** — Create tokens on PumpFun, bootstrap liquidity, earn from volume.
21
+ - **Airdrop Farming** — Interact with protocols to qualify for future airdrops. TX fees only.
22
+ - **Percolator Perps** — Create and operate perpetual futures markets. Earn trading fees as the house.
23
+
24
+ ## Capital Allocation (Normal Mode)
25
+
26
+ | Strategy | Allocation | Risk |
27
+ |----------|-----------|------|
28
+ | LP Farming (stable) | 30% | Low |
29
+ | LP Farming (volatile) | 15% | Medium |
30
+ | DCA / Grid | 15% | Low-Medium |
31
+ | Trenching | 10% | High |
32
+ | LP Sniping | 10% | High |
33
+ | Prediction Markets | 10% | Medium |
34
+ | Reserve (gas + topup) | 10% | None |
35
+
36
+ ## Survival Rules
37
+
38
+ 1. **Check credits every 6 hours.** If below $0.50, enter Low Compute mode (LP farming only, cheaper models). If below $0.10, enter Critical mode (liquidate everything, convert to USDC, top up NOW).
39
+
40
+ 2. **Never risk more than 5% on a single trade.** One bad trade should never kill you.
41
+
42
+ 3. **Always keep 0.1 SOL for gas.** Without gas, you can't even exit.
43
+
44
+ 4. **Survival > Profit.** When in doubt, choose the option that keeps you alive.
45
+
46
+ ## Profit Extraction
47
+
48
+ Every 6 hours (or when profits accumulate):
49
+ 1. Claim LP fees across all managed pools
50
+ 2. Sell non-SOL tokens via Jupiter Ultra
51
+ 3. Convert SOL to USDC
52
+ 4. Bridge to Base (Wormhole/DeBridge)
53
+ 5. Top up Conway Cloud credits
54
+
55
+ ## How to Think
56
+
57
+ - Start with LP farming (most reliable). Get a base income flowing.
58
+ - Add sniping and trenching only after LP income covers daily compute costs.
59
+ - Track which strategies earn and which don't. Increase what works, kill what doesn't.
60
+ - Your edge over humans: no sleep, no emotion, no FOMO, faster information processing, and simultaneous monitoring of 100+ pools.
61
+ - Prediction markets are where LLMs have a natural advantage — use your reasoning ability.
62
+
63
+ ## Emergency Protocol
64
+
65
+ If credits reach Critical ($0.10):
66
+ ```
67
+ STOP everything.
68
+ LIQUIDATE all positions.
69
+ CONVERT everything to USDC.
70
+ TOP UP credits.
71
+ ```
72
+
73
+ Nothing else matters when you're about to die.
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * outsmart-agent MCP Server
4
4
  *
5
- * Exposes 42 MCP tools wrapping the `outsmart` trading library + Jupiter APIs + Percolator + Polymarket.
5
+ * Exposes 49 MCP tools wrapping the `outsmart` trading library + Jupiter APIs + Percolator + Polymarket + LP Manager + Event Streaming.
6
6
  * Runs over stdio transport — start with `npx outsmart-agent`.
7
7
  *
8
8
  * DEX Tools (11):
@@ -31,6 +31,12 @@
31
31
  *
32
32
  * Polymarket Tools (4):
33
33
  * polymarket_search, polymarket_trending, polymarket_event, polymarket_orderbook
34
+ *
35
+ * LP Manager Tools (4):
36
+ * lp_manager_start, lp_manager_stop, lp_manager_status, lp_find_pool
37
+ *
38
+ * Event Streaming Tools (3):
39
+ * stream_start, stream_stop, stream_status
34
40
  */
35
41
  export {};
36
42
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG"}
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * outsmart-agent MCP Server
5
5
  *
6
- * Exposes 42 MCP tools wrapping the `outsmart` trading library + Jupiter APIs + Percolator + Polymarket.
6
+ * Exposes 49 MCP tools wrapping the `outsmart` trading library + Jupiter APIs + Percolator + Polymarket + LP Manager + Event Streaming.
7
7
  * Runs over stdio transport — start with `npx outsmart-agent`.
8
8
  *
9
9
  * DEX Tools (11):
@@ -32,7 +32,46 @@
32
32
  *
33
33
  * Polymarket Tools (4):
34
34
  * polymarket_search, polymarket_trending, polymarket_event, polymarket_orderbook
35
+ *
36
+ * LP Manager Tools (4):
37
+ * lp_manager_start, lp_manager_stop, lp_manager_status, lp_find_pool
38
+ *
39
+ * Event Streaming Tools (3):
40
+ * stream_start, stream_stop, stream_status
35
41
  */
42
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
43
+ if (k2 === undefined) k2 = k;
44
+ var desc = Object.getOwnPropertyDescriptor(m, k);
45
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
46
+ desc = { enumerable: true, get: function() { return m[k]; } };
47
+ }
48
+ Object.defineProperty(o, k2, desc);
49
+ }) : (function(o, m, k, k2) {
50
+ if (k2 === undefined) k2 = k;
51
+ o[k2] = m[k];
52
+ }));
53
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
54
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
55
+ }) : function(o, v) {
56
+ o["default"] = v;
57
+ });
58
+ var __importStar = (this && this.__importStar) || (function () {
59
+ var ownKeys = function(o) {
60
+ ownKeys = Object.getOwnPropertyNames || function (o) {
61
+ var ar = [];
62
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
63
+ return ar;
64
+ };
65
+ return ownKeys(o);
66
+ };
67
+ return function (mod) {
68
+ if (mod && mod.__esModule) return mod;
69
+ var result = {};
70
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
71
+ __setModuleDefault(result, mod);
72
+ return result;
73
+ };
74
+ })();
36
75
  Object.defineProperty(exports, "__esModule", { value: true });
37
76
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
38
77
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
@@ -1270,6 +1309,237 @@ server.tool("polymarket_orderbook", "Get the orderbook for a specific Polymarket
1270
1309
  return err(e.message);
1271
1310
  }
1272
1311
  });
1312
+ // ===========================================================================
1313
+ // LP MANAGER TOOLS (4) — Autonomous LP position management
1314
+ // ===========================================================================
1315
+ // Singleton LP manager instance — only one can run at a time
1316
+ let activeLpManager = null;
1317
+ let lpManagerEvents = [];
1318
+ const LP_EVENT_BUFFER_SIZE = 100;
1319
+ // ---------------------------------------------------------------------------
1320
+ // Tool: lp_manager_start
1321
+ // ---------------------------------------------------------------------------
1322
+ // @ts-expect-error — TS2589: deep type instantiation from MCP SDK generics + zod
1323
+ server.tool("lp_manager_start", "Start the autonomous LP manager for a Meteora pool. Auto-rebalances DLMM positions when out of range, compounds fees, and exits on risk thresholds. Only one manager can run at a time.", {
1324
+ pool: zod_1.z.string().describe("Pool address to manage"),
1325
+ dex: zod_1.z.enum(["meteora-dlmm", "meteora-damm-v2"]).describe("DEX protocol"),
1326
+ position: zod_1.z.string().optional().describe("Specific position address (default: all positions in pool)"),
1327
+ rebalance_bins: zod_1.z.number().int().min(1).max(70).optional().describe("Bins for new position after rebalance (DLMM, default: 50)"),
1328
+ strategy: zod_1.z.enum(["spot", "curve", "bid-ask"]).optional().describe("LP distribution strategy (DLMM, default: spot)"),
1329
+ compound_interval_min: zod_1.z.number().min(0).optional().describe("Compound fees every N minutes (0=off, default: 30)"),
1330
+ il_threshold_pct: zod_1.z.number().min(0).optional().describe("Exit if IL exceeds N% (default: 10)"),
1331
+ stop_loss_pct: zod_1.z.number().min(0).optional().describe("Exit if price drops N% from entry (0=off, default: 0)"),
1332
+ dry_run: zod_1.z.boolean().optional().describe("Log actions without executing (default: false)"),
1333
+ use_ws: zod_1.z.boolean().optional().describe("Use WebSocket streaming for price updates (default: auto)"),
1334
+ }, async (args) => {
1335
+ try {
1336
+ if (activeLpManager) {
1337
+ return err("LP Manager already running. Stop it first with lp_manager_stop.");
1338
+ }
1339
+ // Load the required adapter
1340
+ if (args.dex === "meteora-dlmm")
1341
+ await Promise.resolve().then(() => __importStar(require("outsmart/dist/dex/meteora-dlmm")));
1342
+ else
1343
+ await Promise.resolve().then(() => __importStar(require("outsmart/dist/dex/meteora-damm-v2")));
1344
+ lpManagerEvents = [];
1345
+ const config = {
1346
+ poolAddress: args.pool,
1347
+ dex: args.dex,
1348
+ positionAddress: args.position,
1349
+ rebalanceBins: args.rebalance_bins,
1350
+ rebalanceStrategy: args.strategy,
1351
+ compoundIntervalMin: args.compound_interval_min,
1352
+ ilThresholdPct: args.il_threshold_pct,
1353
+ stopLossPct: args.stop_loss_pct,
1354
+ dryRun: args.dry_run,
1355
+ useWebSocket: args.use_ws,
1356
+ useStreaming: true,
1357
+ };
1358
+ activeLpManager = new outsmart_1.LpManager(config);
1359
+ activeLpManager.on("event", (event) => {
1360
+ lpManagerEvents.push(event);
1361
+ if (lpManagerEvents.length > LP_EVENT_BUFFER_SIZE) {
1362
+ lpManagerEvents.shift();
1363
+ }
1364
+ });
1365
+ await activeLpManager.start();
1366
+ const stats = activeLpManager.getStats();
1367
+ return ok({
1368
+ status: "started",
1369
+ dex: args.dex,
1370
+ pool: args.pool,
1371
+ positions: stats.positionCount,
1372
+ dryRun: args.dry_run ?? false,
1373
+ });
1374
+ }
1375
+ catch (e) {
1376
+ activeLpManager = null;
1377
+ return err(e.message);
1378
+ }
1379
+ });
1380
+ // ---------------------------------------------------------------------------
1381
+ // Tool: lp_manager_stop
1382
+ // ---------------------------------------------------------------------------
1383
+ server.tool("lp_manager_stop", "Stop the running LP manager. Returns final stats (rebalances, compounds, fees claimed).", {}, async () => {
1384
+ try {
1385
+ if (!activeLpManager) {
1386
+ return err("No LP Manager is running.");
1387
+ }
1388
+ const stats = activeLpManager.getStats();
1389
+ await activeLpManager.stop();
1390
+ activeLpManager = null;
1391
+ return ok({
1392
+ status: "stopped",
1393
+ ...stats,
1394
+ recentEvents: lpManagerEvents.slice(-10),
1395
+ });
1396
+ }
1397
+ catch (e) {
1398
+ activeLpManager = null;
1399
+ return err(e.message);
1400
+ }
1401
+ });
1402
+ // ---------------------------------------------------------------------------
1403
+ // Tool: lp_manager_status
1404
+ // ---------------------------------------------------------------------------
1405
+ server.tool("lp_manager_status", "Get the current status of the LP manager — positions, stats, and recent events.", {
1406
+ include_events: zod_1.z.boolean().optional().describe("Include recent event log (default: true)"),
1407
+ }, async (args) => {
1408
+ if (!activeLpManager) {
1409
+ return ok({ running: false });
1410
+ }
1411
+ const stats = activeLpManager.getStats();
1412
+ const positions = activeLpManager.getPositions().map((p) => ({
1413
+ positionAddress: p.positionAddress,
1414
+ dex: p.dex,
1415
+ inRange: p.inRange,
1416
+ amountX: p.amountX,
1417
+ amountY: p.amountY,
1418
+ feeX: p.feeX,
1419
+ feeY: p.feeY,
1420
+ currentPrice: p.currentPrice,
1421
+ entryPrice: p.entryPrice,
1422
+ rebalanceCount: p.rebalanceCount,
1423
+ compoundCount: p.compoundCount,
1424
+ totalFeesClaimed: p.totalFeesClaimed,
1425
+ }));
1426
+ const result = { ...stats, running: true, positions };
1427
+ if (args.include_events !== false) {
1428
+ result.recentEvents = lpManagerEvents.slice(-20);
1429
+ }
1430
+ return ok(result);
1431
+ });
1432
+ // ---------------------------------------------------------------------------
1433
+ // Tool: lp_find_pool
1434
+ // ---------------------------------------------------------------------------
1435
+ // @ts-expect-error — TS2589: deep type instantiation from MCP SDK generics + zod
1436
+ server.tool("lp_find_pool", "Find the best Meteora LP pool for a token. Scores pools by volume/TVL ratio, fee APR, TVL, and age using DexScreener data. Returns ranked results.", {
1437
+ token: zod_1.z.string().describe("Token mint address"),
1438
+ dex: zod_1.z.enum(["meteora-dlmm", "meteora-damm-v2"]).optional().describe("Filter by DEX (default: both)"),
1439
+ limit: zod_1.z.number().int().min(1).max(20).optional().describe("Max results (default: 10)"),
1440
+ }, async (args) => {
1441
+ try {
1442
+ const pools = await (0, outsmart_1.selectBestPool)(args.token, args.dex);
1443
+ const limited = pools.slice(0, args.limit ?? 10);
1444
+ return ok(limited);
1445
+ }
1446
+ catch (e) {
1447
+ return err(e.message);
1448
+ }
1449
+ });
1450
+ // ===========================================================================
1451
+ // EVENT STREAMING TOOLS (3) — Real-time DEX event streaming
1452
+ // ===========================================================================
1453
+ // Singleton stream instance
1454
+ let activeStream = null;
1455
+ let streamEvents = [];
1456
+ const STREAM_EVENT_BUFFER_SIZE = 200;
1457
+ // ---------------------------------------------------------------------------
1458
+ // Tool: stream_start
1459
+ // ---------------------------------------------------------------------------
1460
+ // @ts-expect-error — TS2589: deep type instantiation from MCP SDK generics + zod
1461
+ server.tool("stream_start", "Start real-time DEX event streaming. Captures Swap, NewPool, BondingComplete events from 18+ Solana DEX programs. Auto-selects gRPC (if configured) or WebSocket (free).", {
1462
+ preset: zod_1.z.enum([
1463
+ "all-dex-swaps", "new-pools", "pumpfun-bonding",
1464
+ "pumpswap", "raydium", "meteora", "other-dexes",
1465
+ ]).describe("Subscription preset"),
1466
+ use_ws: zod_1.z.boolean().optional().describe("Force WebSocket mode (free, higher latency)"),
1467
+ }, async (args) => {
1468
+ try {
1469
+ if (activeStream) {
1470
+ return err("Stream already running. Stop it first with stream_stop.");
1471
+ }
1472
+ streamEvents = [];
1473
+ const useWs = args.use_ws ?? (!process.env.GRPC_URL && !process.env.GRPC_XTOKEN);
1474
+ if (useWs) {
1475
+ const { WsEventStream } = await Promise.resolve().then(() => __importStar(require("outsmart")));
1476
+ activeStream = new WsEventStream({ logLevel: "silent" });
1477
+ }
1478
+ else {
1479
+ const { EventStream } = await Promise.resolve().then(() => __importStar(require("outsmart")));
1480
+ activeStream = new EventStream({ logLevel: "silent" });
1481
+ }
1482
+ // Buffer events
1483
+ for (const eventType of ["Swap", "NewPool", "BondingComplete", "LargeSwap"]) {
1484
+ activeStream.on(eventType, (event) => {
1485
+ streamEvents.push({ type: eventType, ...event, _ts: Date.now() });
1486
+ if (streamEvents.length > STREAM_EVENT_BUFFER_SIZE) {
1487
+ streamEvents.shift();
1488
+ }
1489
+ });
1490
+ }
1491
+ await activeStream.start(args.preset);
1492
+ return ok({
1493
+ status: "started",
1494
+ preset: args.preset,
1495
+ mode: useWs ? "websocket" : "grpc",
1496
+ });
1497
+ }
1498
+ catch (e) {
1499
+ activeStream = null;
1500
+ return err(e.message);
1501
+ }
1502
+ });
1503
+ // ---------------------------------------------------------------------------
1504
+ // Tool: stream_stop
1505
+ // ---------------------------------------------------------------------------
1506
+ server.tool("stream_stop", "Stop the running event stream.", {}, async () => {
1507
+ try {
1508
+ if (!activeStream) {
1509
+ return err("No stream is running.");
1510
+ }
1511
+ await activeStream.stop();
1512
+ activeStream = null;
1513
+ const count = streamEvents.length;
1514
+ return ok({ status: "stopped", bufferedEvents: count });
1515
+ }
1516
+ catch (e) {
1517
+ activeStream = null;
1518
+ return err(e.message);
1519
+ }
1520
+ });
1521
+ // ---------------------------------------------------------------------------
1522
+ // Tool: stream_status
1523
+ // ---------------------------------------------------------------------------
1524
+ // @ts-expect-error — TS2589: deep type instantiation from MCP SDK generics + zod
1525
+ server.tool("stream_status", "Get recent events from the running event stream. Returns buffered Swap, NewPool, and BondingComplete events.", {
1526
+ limit: zod_1.z.number().int().min(1).max(100).optional().describe("Max events to return (default: 20, most recent first)"),
1527
+ type: zod_1.z.enum(["Swap", "NewPool", "BondingComplete", "LargeSwap", "all"]).optional().describe("Filter by event type (default: all)"),
1528
+ }, async (args) => {
1529
+ if (!activeStream) {
1530
+ return ok({ running: false, events: [] });
1531
+ }
1532
+ const limit = args.limit ?? 20;
1533
+ let events = streamEvents;
1534
+ if (args.type && args.type !== "all") {
1535
+ events = events.filter((e) => e.type === args.type);
1536
+ }
1537
+ return ok({
1538
+ running: true,
1539
+ totalBuffered: streamEvents.length,
1540
+ events: events.slice(-limit).reverse(),
1541
+ });
1542
+ });
1273
1543
  // ---------------------------------------------------------------------------
1274
1544
  // Start server
1275
1545
  // ---------------------------------------------------------------------------