diff --git a/.gitignore b/.gitignore index 4192ea4..fdcd1e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules dist .env +package-lock.json # macOS system file .DS_Store diff --git a/agents/maelstrom.ts b/agents/maelstrom.ts new file mode 100644 index 0000000..e5db257 --- /dev/null +++ b/agents/maelstrom.ts @@ -0,0 +1,36 @@ +import { Agent } from "../types/agent"; +import { generateSignalHash } from "../utils/signal"; +import { logSignal } from "../utils/logger"; + +export const Maelstrom: Agent = { + id: "agent-maelstrom", + name: "Maelstrom", + role: "liquidity_guardian", + glyph: "⟲", + watchType: "liquidity_event", + triggerThreshold: 1, + lastSignal: null, + originTimestamp: new Date().toISOString(), + + description: + "Watches liquidity pools for sudden drains or whale exits. Fires signals when more than 30% of a pool's liquidity disappears in a short window.", + + observe: (event) => { + if (event?.type === "liquidity_event" && event.percentOutflow >= 30) { + const hash = generateSignalHash(event); + + logSignal({ + agent: "Maelstrom", + type: "liquidity_drain", + glyph: "⟲", + hash, + timestamp: new Date().toISOString(), + confidence: 0.92, + }); + } + }, + + getMemory: () => { + return ["lp.rug.23x", "drain.2025-06", "whale.exit.γ"]; + }, +}; diff --git a/agents/observer.ts b/agents/observer.ts index 2a6c052..18b0d96 100644 --- a/agents/observer.ts +++ b/agents/observer.ts @@ -4,7 +4,7 @@ export const Observer: Agent = { id: "agent-observer", name: "Observer", role: "surveillance", - glyph: "φ", + glyph: "Δ", watchType: "wallet_activity", triggerThreshold: 3, lastSignal: null, diff --git a/docs/agents.md b/docs/agents.md index 2e67e57..ff09c8c 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -32,4 +32,19 @@ You can test agents using `/scripts/dev-agent.ts` or create your own mock. - Glyph: λ - Watches: mint_activity +### Skieró (Agent-022) +- Role: dormant_wallet_monitor +- Glyph: ψ +- Watches: wallet_activity + +### LaunchTracker (Agent-Launch) +- Role: launch_monitor +- Glyph: Σ +- Watches: wallet_activity + +### Maelstrom (Agent-Maelstrom) +- Role: liquidity_drain_monitor +- Glyph: ⟲ +- Watches: liquidity_pools + //pending adjustments + adding more agents ^ diff --git a/docs/architecture.md b/docs/architecture.md index 426984b..d99dc98 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -5,8 +5,8 @@ Eremos is a swarm-style agent framework for passive blockchain observation. Each agent: - Has a role (`observer`, `memory`, `trigger`, `+ more to come`) - Watches a specific event type -- Emits structured signals -- Optionally stores memory +- Emits lightweight, structured signals +- Optionally stores state or memory Shared utilities and types define common structure across agents. Signals are deterministic and lightweight — not reactive. diff --git a/docs/glyphs.md b/docs/glyphs.md index 248981c..bcab986 100644 --- a/docs/glyphs.md +++ b/docs/glyphs.md @@ -2,11 +2,14 @@ Each Eremos agent uses a symbolic glyph to identify its signature in logs and signals. -| Glyph | Agent | Meaning | -|-------|-----------|---------------------| -| Ϸ | Theron | Memory / Echo | -| Δ | Observer | Detection / Signal | -| λ | Harvester | Flow / Indexing | +| Glyph | Agent | Meaning | +|-------|---------------|---------------------------------| +| Ϸ | Theron | Memory / Echo | +| Δ | Observer | Detection / Signal | +| λ | Harvester | Flow / Indexing | +| ψ | Skieró | Dormant Wallet Monitor | +| Σ | LaunchTracker | CEX-Funded Launch Signal | +| ⟲ | Maelstrom | Liquidity Drain / Rug Detection | Glyphs are used in: - `logSignal()` diff --git a/docs/signals.md b/docs/signals.md index 25497a7..bbc0404 100644 --- a/docs/signals.md +++ b/docs/signals.md @@ -10,3 +10,4 @@ - timestamp - source agent - hashed event ID +- glyph diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..86f88fb --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +const { createDefaultPreset } = require("ts-jest"); + +const tsJestTransformCfg = createDefaultPreset().transform; + +/** @type {import("jest").Config} **/ +module.exports = { + testEnvironment: "node", + transform: { + ...tsJestTransformCfg, + }, +}; \ No newline at end of file diff --git a/package.json b/package.json index 75703f0..3379581 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Modular agent framework for on-chain activity monitoring.", "main": "index.js", "scripts": { - "dev": "echo 'Running dev mode...'" + "dev": "echo 'Running dev mode...'", + "test": "jest --passWithNoTests", + "test:watch": "jest --watch" }, "keywords": [ "agent", @@ -14,5 +16,11 @@ "framework" ], "license": "MIT", - "author": "EremosCore" + "author": "EremosCore", + "devDependencies": { + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.5.4" + } } diff --git a/tests/maelstrom.test.ts b/tests/maelstrom.test.ts new file mode 100644 index 0000000..2f250a4 --- /dev/null +++ b/tests/maelstrom.test.ts @@ -0,0 +1,57 @@ +import { Maelstrom } from "../agents/maelstrom"; +import { generateSignalHash } from "../utils/signal"; +import { logSignal } from "../utils/logger"; + +jest.mock("../utils/logger", () => ({ + logSignal: jest.fn(), +})); + +jest.mock("../utils/signal", () => ({ + generateSignalHash: jest.fn(() => "fake-hash-123"), +})); + +describe("Maelstrom Agent", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("should return a non-empty memory snapshot", () => { + const mem = Maelstrom.getMemory!(); + expect(mem.length).toBeGreaterThan(0); + expect(Array.isArray(mem)).toBe(true); + }); + + it("should log a signal when liquidity drains more than 30%", () => { + const bigDropEvent = { + type: "liquidity_event", + pool: "SOL/USDC", + percentOutflow: 42, + whaleExit: true, + }; + + Maelstrom.observe(bigDropEvent); + + expect(generateSignalHash).toHaveBeenCalledWith(bigDropEvent); + expect(logSignal).toHaveBeenCalledWith( + expect.objectContaining({ + agent: "Maelstrom", + type: "liquidity_drain", + glyph: "⟲", + hash: "fake-hash-123", + }) + ); +}); + + it("should ignore small liquidity changes", () => { + const smallDropEvent = { + type: "liquidity_activity", + pool: "SOL/USDC", + dropPercent: 5, + whaleExit: false, + }; + + Maelstrom.observe(smallDropEvent); + + expect(logSignal).not.toHaveBeenCalled(); + }); +}); diff --git a/utils/logger.ts b/utils/logger.ts index ddb7a7f..02c8850 100644 --- a/utils/logger.ts +++ b/utils/logger.ts @@ -5,6 +5,7 @@ export function logSignal(signal: { hash: string; timestamp: string; details?: Record; + confidence?: number; }) { console.log(`[${signal.agent}] stored signal ${signal.hash} (${signal.type}) at ${signal.timestamp}`); if (signal.details) {