nanosolana 0.1.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/README.md +163 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-FJ2L6IFK.js +303 -0
- package/dist/chunk-FJ2L6IFK.js.map +1 -0
- package/dist/chunk-OOHKF4EO.js +3071 -0
- package/dist/chunk-OOHKF4EO.js.map +1 -0
- package/dist/cli/entry.d.ts +1 -0
- package/dist/cli/entry.js +1808 -0
- package/dist/cli/entry.js.map +1 -0
- package/dist/engine-PP6QEVQR.js +8 -0
- package/dist/engine-PP6QEVQR.js.map +1 -0
- package/dist/index.d.ts +1556 -0
- package/dist/index.js +743 -0
- package/dist/index.js.map +1 -0
- package/package.json +88 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
███╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ███████╗ ██████╗ ██╗ █████╗ ███╗ ██╗ █████╗
|
|
5
|
+
████╗ ██║██╔══██╗████╗ ██║██╔═══██╗██╔════╝██╔═══██╗██║ ██╔══██╗████╗ ██║██╔══██╗
|
|
6
|
+
██╔██╗ ██║███████║██╔██╗ ██║██║ ██║███████╗██║ ██║██║ ███████║██╔██╗ ██║███████║
|
|
7
|
+
██║╚██╗██║██╔══██║██║╚██╗██║██║ ██║╚════██║██║ ██║██║ ██╔══██║██║╚██╗██║██╔══██║
|
|
8
|
+
██║ ╚████║██║ ██║██║ ╚████║╚██████╔╝███████║╚██████╔╝███████╗██║ ██║██║ ╚████║██║ ██║
|
|
9
|
+
╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**The Open-Source Agentic Framework for Financial Intelligence on Solana**
|
|
13
|
+
|
|
14
|
+
[](LICENSE)
|
|
15
|
+
[](https://solana.com)
|
|
16
|
+
[](https://npmjs.com/package/nanosolana)
|
|
17
|
+
|
|
18
|
+
[Website](https://nanosolana.com) · [Hub](https://hub.nanosolana.com) · [Docs](https://docs.nanosolana.com) · [GitHub](https://github.com/x402agent/NanoSolana)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
## One-Shot Deploy
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx nanosolana go
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's it — one command handles API key setup, wallet creation, blockchain scan, on-chain NFT identity, OODA trading loop, and gateway.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g nanosolana
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or step-by-step:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
nanosolana init # Configure API keys (encrypted at rest)
|
|
40
|
+
nanosolana birth # Create Solana wallet + mint Birth Certificate NFT + blockchain scan
|
|
41
|
+
nanosolana run # Start the OODA trading loop
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Architecture
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
nano-core/
|
|
48
|
+
├── src/
|
|
49
|
+
│ ├── ai/ → OpenRouter AI provider (multimodal: text, image, audio, video)
|
|
50
|
+
│ ├── cli/ → `nanosolana` CLI (25+ commands)
|
|
51
|
+
│ ├── config/ → AES-256-GCM encrypted vault & Zod-validated config
|
|
52
|
+
│ ├── gateway/ → HMAC-SHA256 authenticated WebSocket + HTTP server
|
|
53
|
+
│ ├── hub/ → NanoHub bridge for UI communication
|
|
54
|
+
│ ├── memory/ → ClawVault 3-tier epistemological memory engine
|
|
55
|
+
│ ├── network/ → Tailscale + tmux mesh networking
|
|
56
|
+
│ ├── nft/ → Metaplex gasless devnet birth certificate NFT
|
|
57
|
+
│ ├── onchain/ → Helius blockchain reader (DAS, Enhanced Tx, wallet scan)
|
|
58
|
+
│ ├── registry/ → On-chain agent identity (Metaplex NFT registration)
|
|
59
|
+
│ ├── nanobot/ → Interactive local web UI companion
|
|
60
|
+
│ ├── pet/ → TamaGOchi virtual pet engine (mood × risk)
|
|
61
|
+
│ ├── strategy/ → RSI + EMA + ATR auto-optimizer
|
|
62
|
+
│ ├── telegram/ → Persistent conversation store (200 msg/chat)
|
|
63
|
+
│ ├── trading/ → OODA trading engine + Jupiter swap execution
|
|
64
|
+
│ └── wallet/ → Solana Ed25519 wallet manager
|
|
65
|
+
├── SOUL.md → Agent identity system prompt
|
|
66
|
+
└── extensions/ → 14+ plugins (Telegram, Discord, Nostr, etc.)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
| Command | Description |
|
|
72
|
+
|---------|-------------|
|
|
73
|
+
| `nanosolana go` | **One-shot: init + birth + scan + register + trade** |
|
|
74
|
+
| `nanosolana init` | Configure + encrypt API keys |
|
|
75
|
+
| `nanosolana birth` | Create wallet + mint Birth Certificate NFT + blockchain scan |
|
|
76
|
+
| `nanosolana run` | Start OODA trading loop |
|
|
77
|
+
| `nanosolana scan [address]` | **Blockchain data scan — SOL, tokens, NFTs, tx history** |
|
|
78
|
+
| `nanosolana register` | **Mint on-chain agent identity NFT (devnet)** |
|
|
79
|
+
| `nanosolana registry` | **Show on-chain agent identity** |
|
|
80
|
+
| `nanosolana nanobot` | **Launch interactive NanoBot web UI** |
|
|
81
|
+
| `nanosolana dvd` | Floating DVD screensaver 🦞 |
|
|
82
|
+
| `nanosolana lobster` | Animated Unicode lobster mascot |
|
|
83
|
+
| `nanosolana status` | Show agent + wallet + pet status |
|
|
84
|
+
| `nanosolana trade status` | Trading P&L and strategy state |
|
|
85
|
+
| `nanosolana trade signals` | Recent trading signals with confidence |
|
|
86
|
+
| `nanosolana wallet balance` | SOL + SPL token balances |
|
|
87
|
+
| `nanosolana pet status` | TamaGOchi pet mood and evolution |
|
|
88
|
+
| `nanosolana memory search` | Search ClawVault memory |
|
|
89
|
+
| `nanosolana gateway run` | Start WebSocket gateway |
|
|
90
|
+
| `nanosolana channels status` | Check channel connections |
|
|
91
|
+
| `nanosolana vault set <key> <val>` | Store encrypted secret |
|
|
92
|
+
| `nanosolana send <msg>` | One-shot message to nano bots |
|
|
93
|
+
| `nanosolana nodes` | List Tailscale mesh peers |
|
|
94
|
+
| `nanosolana doctor` | Run diagnostics |
|
|
95
|
+
| `nanosolana security audit` | Full security scan |
|
|
96
|
+
|
|
97
|
+
## Required API Keys
|
|
98
|
+
|
|
99
|
+
| Key | Source | Required |
|
|
100
|
+
|-----|--------|----------|
|
|
101
|
+
| `OPENROUTER_API_KEY` | [openrouter.ai](https://openrouter.ai) | ✅ |
|
|
102
|
+
| `HELIUS_RPC_URL` | [helius.dev](https://helius.dev) | ✅ |
|
|
103
|
+
| `HELIUS_API_KEY` | [helius.dev](https://helius.dev) | ✅ |
|
|
104
|
+
| `HELIUS_WSS_URL` | [helius.dev](https://helius.dev) | Recommended |
|
|
105
|
+
| `BIRDEYE_API_KEY` | [birdeye.so](https://birdeye.so) | Recommended |
|
|
106
|
+
| `JUPITER_API_KEY` | [jup.ag](https://jup.ag) | For trading |
|
|
107
|
+
|
|
108
|
+
## Security
|
|
109
|
+
|
|
110
|
+
- ✅ AES-256-GCM encrypted secrets vault
|
|
111
|
+
- ✅ HMAC-SHA256 gateway authentication
|
|
112
|
+
- ✅ Ed25519 wallet signatures
|
|
113
|
+
- ✅ Timing-safe token comparison
|
|
114
|
+
- ✅ Rate limiting (10 conn/min, 100 msg/min)
|
|
115
|
+
- ✅ File permissions enforced (0600/0700)
|
|
116
|
+
- ✅ Wallet private key never leaves the vault
|
|
117
|
+
|
|
118
|
+
## Trading Engine (OODA)
|
|
119
|
+
|
|
120
|
+
1. **Observe** — Real-time data from Helius RPC + Birdeye API
|
|
121
|
+
2. **Orient** — AI analysis via OpenRouter (multimodal)
|
|
122
|
+
3. **Decide** — Structured signals with confidence scoring
|
|
123
|
+
4. **Act** — Jupiter swap execution with slippage protection
|
|
124
|
+
5. **Learn** — ClawVault experience replay + contradiction detection
|
|
125
|
+
|
|
126
|
+
## Blockchain Intelligence (Helius)
|
|
127
|
+
|
|
128
|
+
At agent birth, NanoSolana instantly reads the blockchain:
|
|
129
|
+
|
|
130
|
+
- **DAS API** — tokens, NFTs, compressed assets
|
|
131
|
+
- **Enhanced Transactions** — parsed tx history with descriptions
|
|
132
|
+
- **Priority Fees** — real-time fee estimation
|
|
133
|
+
- **Wallet Snapshot** — SOL balance, token prices, NFT inventory
|
|
134
|
+
|
|
135
|
+
## On-Chain Identity
|
|
136
|
+
|
|
137
|
+
Every agent mints a **Metaplex NFT** on devnet as its on-chain identity:
|
|
138
|
+
|
|
139
|
+
- Agent public key
|
|
140
|
+
- NanoSolana version
|
|
141
|
+
- Registered skills
|
|
142
|
+
- SHA-256 fingerprint
|
|
143
|
+
|
|
144
|
+
## TamaGOchi Pet
|
|
145
|
+
|
|
146
|
+
🥚 Egg → 🐛 Larva → 🐣 Juvenile → 🦞 Adult → 👑 Alpha → 👻 Ghost
|
|
147
|
+
|
|
148
|
+
Pet mood affects risk tolerance. Feed to keep alive!
|
|
149
|
+
|
|
150
|
+
## Chrome Extension
|
|
151
|
+
|
|
152
|
+
Manifest V3 browser extension for tab relay, wallet management, chat, and manual trades via the gateway.
|
|
153
|
+
|
|
154
|
+
## Links
|
|
155
|
+
|
|
156
|
+
- **Website:** [nanosolana.com](https://nanosolana.com)
|
|
157
|
+
- **Hub:** [hub.nanosolana.com](https://hub.nanosolana.com)
|
|
158
|
+
- **Docs:** [docs.nanosolana.com](https://docs.nanosolana.com)
|
|
159
|
+
- **GitHub:** [github.com/x402agent/NanoSolana](https://github.com/x402agent/NanoSolana)
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT — [NanoSolana Labs](https://nanosolana.com)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// src/memory/engine.ts
|
|
2
|
+
import { EventEmitter } from "eventemitter3";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var MemoryEngine = class extends EventEmitter {
|
|
8
|
+
constructor(temporalDecayHours = 168, dbPath) {
|
|
9
|
+
super();
|
|
10
|
+
this.temporalDecayHours = temporalDecayHours;
|
|
11
|
+
this.dbPath = dbPath ?? join(homedir(), ".nanosolana", "memory");
|
|
12
|
+
this.ensureDbDir();
|
|
13
|
+
this.loadFromDisk();
|
|
14
|
+
}
|
|
15
|
+
memories = /* @__PURE__ */ new Map();
|
|
16
|
+
lessons = /* @__PURE__ */ new Map();
|
|
17
|
+
decayTimer = null;
|
|
18
|
+
persistTimer = null;
|
|
19
|
+
dbPath;
|
|
20
|
+
/**
|
|
21
|
+
* Store a new memory.
|
|
22
|
+
*/
|
|
23
|
+
store(params) {
|
|
24
|
+
const memory = {
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
type: params.type,
|
|
27
|
+
content: params.content,
|
|
28
|
+
metadata: params.metadata ?? {},
|
|
29
|
+
confidence: 1,
|
|
30
|
+
importance: params.importance ?? 0.5,
|
|
31
|
+
accessCount: 0,
|
|
32
|
+
lastAccessedAt: Date.now(),
|
|
33
|
+
createdAt: Date.now(),
|
|
34
|
+
tags: params.tags ?? [],
|
|
35
|
+
linkedMemories: params.linkedMemories ?? []
|
|
36
|
+
};
|
|
37
|
+
this.memories.set(memory.id, memory);
|
|
38
|
+
this.emit("memoryCreated", memory);
|
|
39
|
+
return memory;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Record a trade outcome and learn from it.
|
|
43
|
+
*/
|
|
44
|
+
recordTradeOutcome(params) {
|
|
45
|
+
const pnl = params.exitPrice - params.entryPrice;
|
|
46
|
+
const pnlPercent = params.entryPrice > 0 ? (params.exitPrice - params.entryPrice) / params.entryPrice * 100 : 0;
|
|
47
|
+
const wasCorrect = pnl > 0;
|
|
48
|
+
const memory = this.store({
|
|
49
|
+
type: "outcome",
|
|
50
|
+
content: `Trade ${params.tradeType} ${params.tokenSymbol}: ${wasCorrect ? "WIN" : "LOSS"} (${pnlPercent.toFixed(2)}%). Reasoning: ${params.reasoning}`,
|
|
51
|
+
metadata: {
|
|
52
|
+
signalId: params.signalId,
|
|
53
|
+
executionId: params.executionId,
|
|
54
|
+
tokenMint: params.tokenMint,
|
|
55
|
+
tokenSymbol: params.tokenSymbol,
|
|
56
|
+
tradeType: params.tradeType,
|
|
57
|
+
entryPrice: params.entryPrice,
|
|
58
|
+
exitPrice: params.exitPrice,
|
|
59
|
+
pnl,
|
|
60
|
+
pnlPercent,
|
|
61
|
+
reasoning: params.reasoning,
|
|
62
|
+
wasCorrect
|
|
63
|
+
},
|
|
64
|
+
tags: [
|
|
65
|
+
params.tradeType,
|
|
66
|
+
params.tokenSymbol,
|
|
67
|
+
wasCorrect ? "win" : "loss",
|
|
68
|
+
pnlPercent > 10 ? "big-win" : pnlPercent < -10 ? "big-loss" : "normal"
|
|
69
|
+
],
|
|
70
|
+
importance: Math.abs(pnlPercent) > 5 ? 0.9 : 0.5
|
|
71
|
+
});
|
|
72
|
+
const lesson = this.learnFromOutcome(memory, wasCorrect, pnlPercent, params);
|
|
73
|
+
return { memory, lesson };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Learn a lesson from a trade outcome.
|
|
77
|
+
*/
|
|
78
|
+
learnFromOutcome(memory, wasCorrect, pnlPercent, params) {
|
|
79
|
+
const similarOutcomes = this.searchByTags([params.tradeType, params.tokenSymbol], 10);
|
|
80
|
+
const recentWins = similarOutcomes.filter(
|
|
81
|
+
(m) => m.memory.metadata.wasCorrect === true
|
|
82
|
+
);
|
|
83
|
+
const recentLosses = similarOutcomes.filter(
|
|
84
|
+
(m) => m.memory.metadata.wasCorrect === false
|
|
85
|
+
);
|
|
86
|
+
const winRate = similarOutcomes.length > 0 ? recentWins.length / similarOutcomes.length : 0.5;
|
|
87
|
+
const patternKey = `${params.tradeType}:${params.tokenSymbol}`;
|
|
88
|
+
const existingLesson = this.lessons.get(patternKey);
|
|
89
|
+
if (existingLesson) {
|
|
90
|
+
existingLesson.occurrences++;
|
|
91
|
+
existingLesson.lastOccurrence = Date.now();
|
|
92
|
+
existingLesson.confidenceImpact = wasCorrect ? Math.min(1, existingLesson.confidenceImpact + 0.1) : Math.max(-1, existingLesson.confidenceImpact - 0.15);
|
|
93
|
+
if (!wasCorrect && recentLosses.length >= 3) {
|
|
94
|
+
existingLesson.adjustment = `STOP trading ${params.tokenSymbol} \u2014 ${recentLosses.length} consecutive losses. Win rate: ${(winRate * 100).toFixed(0)}%`;
|
|
95
|
+
}
|
|
96
|
+
this.lessons.set(patternKey, existingLesson);
|
|
97
|
+
this.emit("lessonLearned", existingLesson);
|
|
98
|
+
return existingLesson;
|
|
99
|
+
}
|
|
100
|
+
const lesson = {
|
|
101
|
+
id: randomUUID(),
|
|
102
|
+
pattern: `${params.tradeType} on ${params.tokenSymbol}`,
|
|
103
|
+
action: params.reasoning,
|
|
104
|
+
outcome: wasCorrect ? `Profit: +${pnlPercent.toFixed(2)}%` : `Loss: ${pnlPercent.toFixed(2)}%`,
|
|
105
|
+
adjustment: wasCorrect ? `Continue monitoring ${params.tokenSymbol} \u2014 strategy working` : `Reduce position size or avoid ${params.tokenSymbol} \u2014 reasoning "${params.reasoning}" led to loss`,
|
|
106
|
+
confidenceImpact: wasCorrect ? 0.1 : -0.15,
|
|
107
|
+
occurrences: 1,
|
|
108
|
+
lastOccurrence: Date.now()
|
|
109
|
+
};
|
|
110
|
+
this.lessons.set(patternKey, lesson);
|
|
111
|
+
this.emit("lessonLearned", lesson);
|
|
112
|
+
return lesson;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Search memories by content similarity (basic text match).
|
|
116
|
+
* In production, this would use vector embeddings.
|
|
117
|
+
*/
|
|
118
|
+
search(query, limit = 10) {
|
|
119
|
+
const queryLower = query.toLowerCase();
|
|
120
|
+
const queryWords = queryLower.split(/\s+/);
|
|
121
|
+
const results = [];
|
|
122
|
+
for (const memory of this.memories.values()) {
|
|
123
|
+
const contentLower = memory.content.toLowerCase();
|
|
124
|
+
let matchScore = 0;
|
|
125
|
+
for (const word of queryWords) {
|
|
126
|
+
if (contentLower.includes(word)) {
|
|
127
|
+
matchScore += 1 / queryWords.length;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const relevance = matchScore * memory.confidence * (0.5 + memory.importance * 0.5);
|
|
131
|
+
if (relevance > 0) {
|
|
132
|
+
memory.accessCount++;
|
|
133
|
+
memory.lastAccessedAt = Date.now();
|
|
134
|
+
results.push({ memory, relevance });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return results.sort((a, b) => b.relevance - a.relevance).slice(0, limit);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Search memories by tags.
|
|
141
|
+
*/
|
|
142
|
+
searchByTags(tags, limit = 10) {
|
|
143
|
+
const results = [];
|
|
144
|
+
for (const memory of this.memories.values()) {
|
|
145
|
+
const matchCount = tags.filter((t) => memory.tags.includes(t)).length;
|
|
146
|
+
if (matchCount > 0) {
|
|
147
|
+
const relevance = matchCount / tags.length * memory.confidence;
|
|
148
|
+
results.push({ memory, relevance });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return results.sort((a, b) => b.relevance - a.relevance).slice(0, limit);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Experience Replay — resurface past lessons for current context.
|
|
155
|
+
* Called before making trading decisions.
|
|
156
|
+
*/
|
|
157
|
+
experienceReplay(context) {
|
|
158
|
+
const relevantLessons = [];
|
|
159
|
+
for (const lesson of this.lessons.values()) {
|
|
160
|
+
let relevant = false;
|
|
161
|
+
if (context.tokenSymbol && lesson.pattern.includes(context.tokenSymbol)) {
|
|
162
|
+
relevant = true;
|
|
163
|
+
}
|
|
164
|
+
if (context.tradeType && lesson.pattern.includes(context.tradeType)) {
|
|
165
|
+
relevant = true;
|
|
166
|
+
}
|
|
167
|
+
if (relevant) {
|
|
168
|
+
relevantLessons.push(lesson);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (relevantLessons.length > 0) {
|
|
172
|
+
this.emit("replayTriggered", relevantLessons);
|
|
173
|
+
}
|
|
174
|
+
return relevantLessons;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Apply temporal decay to all memories.
|
|
178
|
+
* Older memories lose confidence unless they're important or frequently accessed.
|
|
179
|
+
*/
|
|
180
|
+
applyDecay() {
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const decayWindowMs = this.temporalDecayHours * 60 * 60 * 1e3;
|
|
183
|
+
for (const [id, memory] of this.memories) {
|
|
184
|
+
const age = now - memory.createdAt;
|
|
185
|
+
const timeSinceAccess = now - memory.lastAccessedAt;
|
|
186
|
+
let decayRate = age / decayWindowMs;
|
|
187
|
+
if (memory.accessCount > 5) decayRate *= 0.5;
|
|
188
|
+
decayRate *= 1 - memory.importance * 0.7;
|
|
189
|
+
const newConfidence = Math.max(0.01, memory.confidence - decayRate * 0.01);
|
|
190
|
+
if (newConfidence !== memory.confidence) {
|
|
191
|
+
memory.confidence = newConfidence;
|
|
192
|
+
if (newConfidence < 0.1) {
|
|
193
|
+
this.emit("memoryDecayed", id);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const [id, memory] of this.memories) {
|
|
198
|
+
if (memory.confidence < 0.05 && memory.importance < 0.3) {
|
|
199
|
+
this.memories.delete(id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Reinforce a memory — called when a past memory proves relevant again.
|
|
205
|
+
*/
|
|
206
|
+
reinforce(id, boost = 0.2) {
|
|
207
|
+
const memory = this.memories.get(id);
|
|
208
|
+
if (!memory) return;
|
|
209
|
+
memory.confidence = Math.min(1, memory.confidence + boost);
|
|
210
|
+
memory.accessCount++;
|
|
211
|
+
memory.lastAccessedAt = Date.now();
|
|
212
|
+
this.emit("memoryReinforced", id, memory.confidence);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Start autonomous memory management.
|
|
216
|
+
*/
|
|
217
|
+
startAutonomous(decayIntervalMs = 6e4, persistIntervalMs = 3e4) {
|
|
218
|
+
this.decayTimer = setInterval(() => this.applyDecay(), decayIntervalMs);
|
|
219
|
+
this.persistTimer = setInterval(() => this.persistToDisk(), persistIntervalMs);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Stop autonomous management.
|
|
223
|
+
*/
|
|
224
|
+
stopAutonomous() {
|
|
225
|
+
if (this.decayTimer) {
|
|
226
|
+
clearInterval(this.decayTimer);
|
|
227
|
+
this.decayTimer = null;
|
|
228
|
+
}
|
|
229
|
+
if (this.persistTimer) {
|
|
230
|
+
clearInterval(this.persistTimer);
|
|
231
|
+
this.persistTimer = null;
|
|
232
|
+
}
|
|
233
|
+
this.persistToDisk();
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get memory stats.
|
|
237
|
+
*/
|
|
238
|
+
getStats() {
|
|
239
|
+
let totalConfidence = 0;
|
|
240
|
+
let tradeWins = 0;
|
|
241
|
+
let totalTrades = 0;
|
|
242
|
+
for (const memory of this.memories.values()) {
|
|
243
|
+
totalConfidence += memory.confidence;
|
|
244
|
+
if (memory.type === "outcome") {
|
|
245
|
+
totalTrades++;
|
|
246
|
+
if (memory.metadata.wasCorrect) tradeWins++;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
totalMemories: this.memories.size,
|
|
251
|
+
totalLessons: this.lessons.size,
|
|
252
|
+
avgConfidence: this.memories.size > 0 ? totalConfidence / this.memories.size : 0,
|
|
253
|
+
tradeWinRate: totalTrades > 0 ? tradeWins / totalTrades : 0
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get all lessons.
|
|
258
|
+
*/
|
|
259
|
+
getLessons() {
|
|
260
|
+
return [...this.lessons.values()];
|
|
261
|
+
}
|
|
262
|
+
// ── Persistence ────────────────────────────────────────────
|
|
263
|
+
ensureDbDir() {
|
|
264
|
+
if (!existsSync(this.dbPath)) {
|
|
265
|
+
mkdirSync(this.dbPath, { recursive: true, mode: 448 });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
persistToDisk() {
|
|
269
|
+
try {
|
|
270
|
+
const memoriesFile = join(this.dbPath, "memories.json");
|
|
271
|
+
const lessonsFile = join(this.dbPath, "lessons.json");
|
|
272
|
+
const memoriesArr = [...this.memories.values()];
|
|
273
|
+
const lessonsArr = [...this.lessons.entries()];
|
|
274
|
+
writeFileSync(memoriesFile, JSON.stringify(memoriesArr, null, 2), { mode: 384 });
|
|
275
|
+
writeFileSync(lessonsFile, JSON.stringify(lessonsArr, null, 2), { mode: 384 });
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
loadFromDisk() {
|
|
280
|
+
try {
|
|
281
|
+
const memoriesFile = join(this.dbPath, "memories.json");
|
|
282
|
+
const lessonsFile = join(this.dbPath, "lessons.json");
|
|
283
|
+
if (existsSync(memoriesFile)) {
|
|
284
|
+
const data = JSON.parse(readFileSync(memoriesFile, "utf8"));
|
|
285
|
+
for (const memory of data) {
|
|
286
|
+
this.memories.set(memory.id, memory);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (existsSync(lessonsFile)) {
|
|
290
|
+
const data = JSON.parse(readFileSync(lessonsFile, "utf8"));
|
|
291
|
+
for (const [key, lesson] of data) {
|
|
292
|
+
this.lessons.set(key, lesson);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export {
|
|
301
|
+
MemoryEngine
|
|
302
|
+
};
|
|
303
|
+
//# sourceMappingURL=chunk-FJ2L6IFK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/memory/engine.ts"],"sourcesContent":["/**\n * Nano Solana — Real-Time Memory Engine\n *\n * Adaptive memory system that learns from trading outcomes.\n * Features:\n * - Temporal decay (older memories fade)\n * - Experience replay (learn from mistakes)\n * - Semantic search via embeddings\n * - Trade outcome feedback loop\n *\n * The memory is \"sentient\" in the sense that it autonomously:\n * 1. Observes outcomes and correlates them with past decisions\n * 2. Adjusts confidence weights based on success/failure\n * 3. Surfaces relevant past experiences during decision-making\n * 4. Prunes low-value memories to stay focused\n */\n\nimport { EventEmitter } from \"eventemitter3\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ── Types ────────────────────────────────────────────────────\n\nexport interface Memory {\n id: string;\n type: \"observation\" | \"decision\" | \"outcome\" | \"lesson\" | \"context\";\n content: string;\n embedding?: number[];\n metadata: Record<string, unknown>;\n confidence: number; // 0-1, decays over time\n importance: number; // 0-1, increased by relevance\n accessCount: number;\n lastAccessedAt: number;\n createdAt: number;\n tags: string[];\n linkedMemories: string[]; // IDs of related memories\n}\n\nexport interface TradeMemory extends Memory {\n type: \"outcome\";\n metadata: {\n signalId: string;\n executionId: string;\n tokenMint: string;\n tokenSymbol: string;\n tradeType: \"buy\" | \"sell\";\n entryPrice: number;\n exitPrice?: number;\n pnl?: number;\n pnlPercent?: number;\n holdDurationMs?: number;\n reasoning: string;\n wasCorrect?: boolean;\n };\n}\n\nexport interface Lesson {\n id: string;\n pattern: string; // What pattern was recognized\n action: string; // What the agent did\n outcome: string; // What happened\n adjustment: string; // What to do differently\n confidenceImpact: number; // How much to adjust confidence (-1 to 1)\n occurrences: number;\n lastOccurrence: number;\n}\n\nexport interface MemorySearchResult {\n memory: Memory;\n relevance: number;\n}\n\nexport interface MemoryEngineEvents {\n memoryCreated: (memory: Memory) => void;\n lessonLearned: (lesson: Lesson) => void;\n memoryDecayed: (id: string) => void;\n memoryReinforced: (id: string, newConfidence: number) => void;\n replayTriggered: (lessons: Lesson[]) => void;\n}\n\n// ── Memory Engine ────────────────────────────────────────────\n\nexport class MemoryEngine extends EventEmitter<MemoryEngineEvents> {\n private memories: Map<string, Memory> = new Map();\n private lessons: Map<string, Lesson> = new Map();\n private decayTimer: ReturnType<typeof setInterval> | null = null;\n private persistTimer: ReturnType<typeof setInterval> | null = null;\n private dbPath: string;\n\n constructor(\n private temporalDecayHours = 168, // 7 days\n dbPath?: string,\n ) {\n super();\n this.dbPath = dbPath ?? join(homedir(), \".nanosolana\", \"memory\");\n this.ensureDbDir();\n this.loadFromDisk();\n }\n\n /**\n * Store a new memory.\n */\n store(params: {\n type: Memory[\"type\"];\n content: string;\n metadata?: Record<string, unknown>;\n tags?: string[];\n importance?: number;\n linkedMemories?: string[];\n }): Memory {\n const memory: Memory = {\n id: randomUUID(),\n type: params.type,\n content: params.content,\n metadata: params.metadata ?? {},\n confidence: 1.0,\n importance: params.importance ?? 0.5,\n accessCount: 0,\n lastAccessedAt: Date.now(),\n createdAt: Date.now(),\n tags: params.tags ?? [],\n linkedMemories: params.linkedMemories ?? [],\n };\n\n this.memories.set(memory.id, memory);\n this.emit(\"memoryCreated\", memory);\n return memory;\n }\n\n /**\n * Record a trade outcome and learn from it.\n */\n recordTradeOutcome(params: {\n signalId: string;\n executionId: string;\n tokenMint: string;\n tokenSymbol: string;\n tradeType: \"buy\" | \"sell\";\n entryPrice: number;\n exitPrice: number;\n reasoning: string;\n }): { memory: Memory; lesson: Lesson | null } {\n const pnl = params.exitPrice - params.entryPrice;\n const pnlPercent = params.entryPrice > 0\n ? ((params.exitPrice - params.entryPrice) / params.entryPrice) * 100\n : 0;\n const wasCorrect = pnl > 0;\n\n // Store the outcome as a memory\n const memory = this.store({\n type: \"outcome\",\n content: `Trade ${params.tradeType} ${params.tokenSymbol}: ${wasCorrect ? \"WIN\" : \"LOSS\"} (${pnlPercent.toFixed(2)}%). Reasoning: ${params.reasoning}`,\n metadata: {\n signalId: params.signalId,\n executionId: params.executionId,\n tokenMint: params.tokenMint,\n tokenSymbol: params.tokenSymbol,\n tradeType: params.tradeType,\n entryPrice: params.entryPrice,\n exitPrice: params.exitPrice,\n pnl,\n pnlPercent,\n reasoning: params.reasoning,\n wasCorrect,\n },\n tags: [\n params.tradeType,\n params.tokenSymbol,\n wasCorrect ? \"win\" : \"loss\",\n pnlPercent > 10 ? \"big-win\" : pnlPercent < -10 ? \"big-loss\" : \"normal\",\n ],\n importance: Math.abs(pnlPercent) > 5 ? 0.9 : 0.5,\n });\n\n // Learn a lesson from the outcome\n const lesson = this.learnFromOutcome(memory, wasCorrect, pnlPercent, params);\n\n return { memory, lesson };\n }\n\n /**\n * Learn a lesson from a trade outcome.\n */\n private learnFromOutcome(\n memory: Memory,\n wasCorrect: boolean,\n pnlPercent: number,\n params: { tokenSymbol: string; tradeType: string; reasoning: string },\n ): Lesson | null {\n // Find similar past outcomes to identify patterns\n const similarOutcomes = this.searchByTags([params.tradeType, params.tokenSymbol], 10);\n const recentWins = similarOutcomes.filter(\n (m) => (m.memory.metadata as any).wasCorrect === true,\n );\n const recentLosses = similarOutcomes.filter(\n (m) => (m.memory.metadata as any).wasCorrect === false,\n );\n\n // Generate a pattern-based lesson\n const winRate = similarOutcomes.length > 0\n ? recentWins.length / similarOutcomes.length\n : 0.5;\n\n const patternKey = `${params.tradeType}:${params.tokenSymbol}`;\n const existingLesson = this.lessons.get(patternKey);\n\n if (existingLesson) {\n // Update existing lesson\n existingLesson.occurrences++;\n existingLesson.lastOccurrence = Date.now();\n existingLesson.confidenceImpact = wasCorrect\n ? Math.min(1, existingLesson.confidenceImpact + 0.1)\n : Math.max(-1, existingLesson.confidenceImpact - 0.15);\n\n if (!wasCorrect && recentLosses.length >= 3) {\n existingLesson.adjustment = `STOP trading ${params.tokenSymbol} — ${recentLosses.length} consecutive losses. Win rate: ${(winRate * 100).toFixed(0)}%`;\n }\n\n this.lessons.set(patternKey, existingLesson);\n this.emit(\"lessonLearned\", existingLesson);\n return existingLesson;\n }\n\n // Create new lesson\n const lesson: Lesson = {\n id: randomUUID(),\n pattern: `${params.tradeType} on ${params.tokenSymbol}`,\n action: params.reasoning,\n outcome: wasCorrect\n ? `Profit: +${pnlPercent.toFixed(2)}%`\n : `Loss: ${pnlPercent.toFixed(2)}%`,\n adjustment: wasCorrect\n ? `Continue monitoring ${params.tokenSymbol} — strategy working`\n : `Reduce position size or avoid ${params.tokenSymbol} — reasoning \"${params.reasoning}\" led to loss`,\n confidenceImpact: wasCorrect ? 0.1 : -0.15,\n occurrences: 1,\n lastOccurrence: Date.now(),\n };\n\n this.lessons.set(patternKey, lesson);\n this.emit(\"lessonLearned\", lesson);\n return lesson;\n }\n\n /**\n * Search memories by content similarity (basic text match).\n * In production, this would use vector embeddings.\n */\n search(query: string, limit = 10): MemorySearchResult[] {\n const queryLower = query.toLowerCase();\n const queryWords = queryLower.split(/\\s+/);\n\n const results: MemorySearchResult[] = [];\n\n for (const memory of this.memories.values()) {\n const contentLower = memory.content.toLowerCase();\n let matchScore = 0;\n\n for (const word of queryWords) {\n if (contentLower.includes(word)) {\n matchScore += 1 / queryWords.length;\n }\n }\n\n // Boost by confidence and importance\n const relevance = matchScore * memory.confidence * (0.5 + memory.importance * 0.5);\n\n if (relevance > 0) {\n // Mark as accessed\n memory.accessCount++;\n memory.lastAccessedAt = Date.now();\n\n results.push({ memory, relevance });\n }\n }\n\n return results\n .sort((a, b) => b.relevance - a.relevance)\n .slice(0, limit);\n }\n\n /**\n * Search memories by tags.\n */\n searchByTags(tags: string[], limit = 10): MemorySearchResult[] {\n const results: MemorySearchResult[] = [];\n\n for (const memory of this.memories.values()) {\n const matchCount = tags.filter((t) => memory.tags.includes(t)).length;\n if (matchCount > 0) {\n const relevance = (matchCount / tags.length) * memory.confidence;\n results.push({ memory, relevance });\n }\n }\n\n return results\n .sort((a, b) => b.relevance - a.relevance)\n .slice(0, limit);\n }\n\n /**\n * Experience Replay — resurface past lessons for current context.\n * Called before making trading decisions.\n */\n experienceReplay(context: {\n tokenSymbol?: string;\n tradeType?: string;\n marketCondition?: string;\n }): Lesson[] {\n const relevantLessons: Lesson[] = [];\n\n for (const lesson of this.lessons.values()) {\n let relevant = false;\n\n if (context.tokenSymbol && lesson.pattern.includes(context.tokenSymbol)) {\n relevant = true;\n }\n if (context.tradeType && lesson.pattern.includes(context.tradeType)) {\n relevant = true;\n }\n\n if (relevant) {\n relevantLessons.push(lesson);\n }\n }\n\n if (relevantLessons.length > 0) {\n this.emit(\"replayTriggered\", relevantLessons);\n }\n\n return relevantLessons;\n }\n\n /**\n * Apply temporal decay to all memories.\n * Older memories lose confidence unless they're important or frequently accessed.\n */\n applyDecay(): void {\n const now = Date.now();\n const decayWindowMs = this.temporalDecayHours * 60 * 60 * 1000;\n\n for (const [id, memory] of this.memories) {\n const age = now - memory.createdAt;\n const timeSinceAccess = now - memory.lastAccessedAt;\n\n // Base decay factor\n let decayRate = age / decayWindowMs;\n\n // Reduce decay for frequently accessed memories\n if (memory.accessCount > 5) decayRate *= 0.5;\n\n // Reduce decay for high-importance memories\n decayRate *= 1 - memory.importance * 0.7;\n\n // Apply decay\n const newConfidence = Math.max(0.01, memory.confidence - decayRate * 0.01);\n\n if (newConfidence !== memory.confidence) {\n memory.confidence = newConfidence;\n\n if (newConfidence < 0.1) {\n this.emit(\"memoryDecayed\", id);\n }\n }\n }\n\n // Prune very old, low-confidence memories (keep the store lean)\n for (const [id, memory] of this.memories) {\n if (memory.confidence < 0.05 && memory.importance < 0.3) {\n this.memories.delete(id);\n }\n }\n }\n\n /**\n * Reinforce a memory — called when a past memory proves relevant again.\n */\n reinforce(id: string, boost = 0.2): void {\n const memory = this.memories.get(id);\n if (!memory) return;\n\n memory.confidence = Math.min(1, memory.confidence + boost);\n memory.accessCount++;\n memory.lastAccessedAt = Date.now();\n\n this.emit(\"memoryReinforced\", id, memory.confidence);\n }\n\n /**\n * Start autonomous memory management.\n */\n startAutonomous(decayIntervalMs = 60000, persistIntervalMs = 30000): void {\n // Temporal decay\n this.decayTimer = setInterval(() => this.applyDecay(), decayIntervalMs);\n\n // Periodic persistence\n this.persistTimer = setInterval(() => this.persistToDisk(), persistIntervalMs);\n }\n\n /**\n * Stop autonomous management.\n */\n stopAutonomous(): void {\n if (this.decayTimer) {\n clearInterval(this.decayTimer);\n this.decayTimer = null;\n }\n if (this.persistTimer) {\n clearInterval(this.persistTimer);\n this.persistTimer = null;\n }\n this.persistToDisk(); // Final save\n }\n\n /**\n * Get memory stats.\n */\n getStats(): {\n totalMemories: number;\n totalLessons: number;\n avgConfidence: number;\n tradeWinRate: number;\n } {\n let totalConfidence = 0;\n let tradeWins = 0;\n let totalTrades = 0;\n\n for (const memory of this.memories.values()) {\n totalConfidence += memory.confidence;\n if (memory.type === \"outcome\") {\n totalTrades++;\n if ((memory.metadata as any).wasCorrect) tradeWins++;\n }\n }\n\n return {\n totalMemories: this.memories.size,\n totalLessons: this.lessons.size,\n avgConfidence: this.memories.size > 0 ? totalConfidence / this.memories.size : 0,\n tradeWinRate: totalTrades > 0 ? tradeWins / totalTrades : 0,\n };\n }\n\n /**\n * Get all lessons.\n */\n getLessons(): Lesson[] {\n return [...this.lessons.values()];\n }\n\n // ── Persistence ────────────────────────────────────────────\n\n private ensureDbDir(): void {\n if (!existsSync(this.dbPath)) {\n mkdirSync(this.dbPath, { recursive: true, mode: 0o700 });\n }\n }\n\n private persistToDisk(): void {\n try {\n const memoriesFile = join(this.dbPath, \"memories.json\");\n const lessonsFile = join(this.dbPath, \"lessons.json\");\n\n const memoriesArr = [...this.memories.values()];\n const lessonsArr = [...this.lessons.entries()];\n\n writeFileSync(memoriesFile, JSON.stringify(memoriesArr, null, 2), { mode: 0o600 });\n writeFileSync(lessonsFile, JSON.stringify(lessonsArr, null, 2), { mode: 0o600 });\n } catch {\n // Silent fail on persistence — memory is primarily in-RAM\n }\n }\n\n private loadFromDisk(): void {\n try {\n const memoriesFile = join(this.dbPath, \"memories.json\");\n const lessonsFile = join(this.dbPath, \"lessons.json\");\n\n if (existsSync(memoriesFile)) {\n const data = JSON.parse(readFileSync(memoriesFile, \"utf8\")) as Memory[];\n for (const memory of data) {\n this.memories.set(memory.id, memory);\n }\n }\n\n if (existsSync(lessonsFile)) {\n const data = JSON.parse(readFileSync(lessonsFile, \"utf8\")) as [string, Lesson][];\n for (const [key, lesson] of data) {\n this.lessons.set(key, lesson);\n }\n }\n } catch {\n // Fresh start if files are corrupted\n }\n }\n}\n"],"mappings":"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","names":[]}
|