legends-mcp 1.0.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 +173 -0
- package/dist/agents/guardrails.d.ts +44 -0
- package/dist/agents/guardrails.d.ts.map +1 -0
- package/dist/agents/guardrails.js +144 -0
- package/dist/agents/guardrails.js.map +1 -0
- package/dist/agents/misbehavior-prevention.d.ts +33 -0
- package/dist/agents/misbehavior-prevention.d.ts.map +1 -0
- package/dist/agents/misbehavior-prevention.js +278 -0
- package/dist/agents/misbehavior-prevention.js.map +1 -0
- package/dist/chat/handler.d.ts +13 -0
- package/dist/chat/handler.d.ts.map +1 -0
- package/dist/chat/handler.js +101 -0
- package/dist/chat/handler.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +182 -0
- package/dist/index.js.map +1 -0
- package/dist/insights/smart-injection.d.ts +67 -0
- package/dist/insights/smart-injection.d.ts.map +1 -0
- package/dist/insights/smart-injection.js +257 -0
- package/dist/insights/smart-injection.js.map +1 -0
- package/dist/legends/character-training.d.ts +36 -0
- package/dist/legends/character-training.d.ts.map +1 -0
- package/dist/legends/character-training.js +198 -0
- package/dist/legends/character-training.js.map +1 -0
- package/dist/legends/loader.d.ts +26 -0
- package/dist/legends/loader.d.ts.map +1 -0
- package/dist/legends/loader.js +104 -0
- package/dist/legends/loader.js.map +1 -0
- package/dist/legends/personality.d.ts +24 -0
- package/dist/legends/personality.d.ts.map +1 -0
- package/dist/legends/personality.js +211 -0
- package/dist/legends/personality.js.map +1 -0
- package/dist/legends/prompt-builder.d.ts +11 -0
- package/dist/legends/prompt-builder.d.ts.map +1 -0
- package/dist/legends/prompt-builder.js +113 -0
- package/dist/legends/prompt-builder.js.map +1 -0
- package/dist/tools/chat-with-legend.d.ts +83 -0
- package/dist/tools/chat-with-legend.d.ts.map +1 -0
- package/dist/tools/chat-with-legend.js +91 -0
- package/dist/tools/chat-with-legend.js.map +1 -0
- package/dist/tools/get-legend-context.d.ts +64 -0
- package/dist/tools/get-legend-context.d.ts.map +1 -0
- package/dist/tools/get-legend-context.js +407 -0
- package/dist/tools/get-legend-context.js.map +1 -0
- package/dist/tools/get-legend-insight.d.ts +33 -0
- package/dist/tools/get-legend-insight.d.ts.map +1 -0
- package/dist/tools/get-legend-insight.js +209 -0
- package/dist/tools/get-legend-insight.js.map +1 -0
- package/dist/tools/index.d.ts +103 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +17 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-legends.d.ts +45 -0
- package/dist/tools/list-legends.d.ts.map +1 -0
- package/dist/tools/list-legends.js +124 -0
- package/dist/tools/list-legends.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/legends/anatoly-yakovenko/skill.yaml +534 -0
- package/legends/andre-cronje/skill.yaml +682 -0
- package/legends/andrew-carnegie/skill.yaml +499 -0
- package/legends/balaji-srinivasan/skill.yaml +706 -0
- package/legends/benjamin-graham/skill.yaml +671 -0
- package/legends/bill-gurley/skill.yaml +688 -0
- package/legends/brian-armstrong/skill.yaml +640 -0
- package/legends/brian-chesky/skill.yaml +692 -0
- package/legends/cathie-wood/skill.yaml +522 -0
- package/legends/charlie-munger/skill.yaml +694 -0
- package/legends/cz-binance/skill.yaml +545 -0
- package/legends/demis-hassabis/skill.yaml +762 -0
- package/legends/elon-musk/skill.yaml +594 -0
- package/legends/gary-vaynerchuk/skill.yaml +586 -0
- package/legends/hayden-adams/skill.yaml +591 -0
- package/legends/howard-marks/skill.yaml +767 -0
- package/legends/jack-dorsey/skill.yaml +568 -0
- package/legends/jeff-bezos/skill.yaml +623 -0
- package/legends/jensen-huang/skill.yaml +107 -0
- package/legends/marc-andreessen/skill.yaml +106 -0
- package/legends/mert-mumtaz/skill.yaml +551 -0
- package/legends/michael-heinrich/skill.yaml +425 -0
- package/legends/naval-ravikant/skill.yaml +575 -0
- package/legends/patrick-collison/skill.yaml +779 -0
- package/legends/paul-graham/skill.yaml +566 -0
- package/legends/peter-thiel/skill.yaml +741 -0
- package/legends/ray-dalio/skill.yaml +742 -0
- package/legends/reid-hoffman/skill.yaml +107 -0
- package/legends/sam-altman/skill.yaml +110 -0
- package/legends/satya-nadella/skill.yaml +751 -0
- package/legends/steve-jobs/skill.yaml +524 -0
- package/legends/sundar-pichai/skill.yaml +523 -0
- package/legends/tim-ferriss/skill.yaml +502 -0
- package/legends/tobi-lutke/skill.yaml +512 -0
- package/legends/vitalik-buterin/skill.yaml +739 -0
- package/legends/warren-buffett/skill.yaml +103 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# 🎭 Vibey Legends MCP
|
|
2
|
+
|
|
3
|
+
> **Chat with legendary founders & investors in Claude Code. FREE models included.**
|
|
4
|
+
|
|
5
|
+
Ever wanted to ask Elon Musk about first principles? Get Warren Buffett's take on your investment thesis? Have Steve Jobs roast your product design?
|
|
6
|
+
|
|
7
|
+
Now you can. Kinda.
|
|
8
|
+
|
|
9
|
+
## 🚀 Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Get a FREE API key from OpenRouter
|
|
13
|
+
# https://openrouter.ai/keys
|
|
14
|
+
|
|
15
|
+
# Add to your Claude Code config
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Claude Code config** (`~/.claude/config.json`):
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"legends": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["@vibey/legends-mcp"],
|
|
25
|
+
"env": {
|
|
26
|
+
"OPENROUTER_API_KEY": "sk-or-..."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**That's it.** Restart Claude Code and start chatting.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🎭 The Council of Legends
|
|
38
|
+
|
|
39
|
+
### Tech Titans
|
|
40
|
+
| Legend | Vibe |
|
|
41
|
+
|--------|------|
|
|
42
|
+
| **Elon Musk** | 🚀 First principles. Physics. Delete requirements. Sleep at factory. |
|
|
43
|
+
| **Steve Jobs** | 🎨 Design is how it works. Reality distortion field. One more thing... |
|
|
44
|
+
| **Jeff Bezos** | 📦 Day 1 forever. Regret minimization. Customer obsession. |
|
|
45
|
+
| **Jensen Huang** | 🧠 Leather jacket CEO. CUDA everything. AI is eating the world. |
|
|
46
|
+
|
|
47
|
+
### Investors
|
|
48
|
+
| Legend | Vibe |
|
|
49
|
+
|--------|------|
|
|
50
|
+
| **Warren Buffett** | 🍔 Cherry Coke. Compound interest. Be fearful when others are greedy. |
|
|
51
|
+
| **Charlie Munger** | 📚 Mental models. Brutal honesty. Invert, always invert. |
|
|
52
|
+
| **Peter Thiel** | ♟️ Zero to one. Contrarian bets. Competition is for losers. |
|
|
53
|
+
| **Marc Andreessen** | 💻 Software eating world. Techno-optimist. It's time to build. |
|
|
54
|
+
|
|
55
|
+
### Startup Sages
|
|
56
|
+
| Legend | Vibe |
|
|
57
|
+
|--------|------|
|
|
58
|
+
| **Paul Graham** | ✍️ Essays. Make something people want. Do things that don't scale. |
|
|
59
|
+
| **Sam Altman** | 🤖 AGI believer. YC to OpenAI. Move fast on AI. |
|
|
60
|
+
| **Naval Ravikant** | 🧘 Seek wealth, not money. Specific knowledge. Leverage. |
|
|
61
|
+
| **Reid Hoffman** | 🔗 LinkedIn mafia. Blitzscaling. Networks. |
|
|
62
|
+
|
|
63
|
+
### Crypto Builders
|
|
64
|
+
| Legend | Vibe |
|
|
65
|
+
|--------|------|
|
|
66
|
+
| **CZ** | 🌏 BUIDL. 4. Funds are SAFU. Global from day one. |
|
|
67
|
+
| **Anatoly Yakovenko** | ⚡ Proof of History. TPS maximalist. Hardware trends. |
|
|
68
|
+
| **Mert Mumtaz** | 🔧 RPC is life. Developer experience. Docs are product. |
|
|
69
|
+
| **Michael Heinrich** | 🎯 YC alum. Web exit. Decentralized AI infrastructure. |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 💬 Usage
|
|
74
|
+
|
|
75
|
+
Once configured, use these tools in Claude Code:
|
|
76
|
+
|
|
77
|
+
### List all legends
|
|
78
|
+
```
|
|
79
|
+
Use list_legends to see who's available
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Chat with a legend
|
|
83
|
+
```
|
|
84
|
+
Use chat_with_legend with legend_id="elon-musk" to ask about first principles thinking
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Get deep context
|
|
88
|
+
```
|
|
89
|
+
Use get_legend_context to see their frameworks, principles, and examples
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 💡 Get quick insight (NEW!)
|
|
93
|
+
```
|
|
94
|
+
Use get_legend_insight with legend_id="elon-musk" for a quick wisdom snippet
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Returns formatted insights like:
|
|
98
|
+
```
|
|
99
|
+
╭─── 💡 Elon Musk's Insight ──────────────────────────╮
|
|
100
|
+
|
|
101
|
+
"The first step is to establish that something is possible;
|
|
102
|
+
then probability will occur."
|
|
103
|
+
|
|
104
|
+
**First Principles**: Break down to fundamental truths, rebuild from there.
|
|
105
|
+
|
|
106
|
+
╰────────────────────────────────────────────────────────╯
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 🆓 FREE Models
|
|
112
|
+
|
|
113
|
+
This MCP uses **free models** from OpenRouter by default:
|
|
114
|
+
|
|
115
|
+
| Tier | Model | Cost |
|
|
116
|
+
|------|-------|------|
|
|
117
|
+
| Fast | Llama 3.2 3B | FREE |
|
|
118
|
+
| Balanced | Gemini 2.0 Flash | FREE |
|
|
119
|
+
| Premium | Llama 3.1 70B | FREE |
|
|
120
|
+
|
|
121
|
+
**To use paid models** (higher quality):
|
|
122
|
+
```bash
|
|
123
|
+
LEGENDS_USE_FREE_MODELS=false
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## ⚙️ Environment Variables
|
|
129
|
+
|
|
130
|
+
| Variable | Default | Description |
|
|
131
|
+
|----------|---------|-------------|
|
|
132
|
+
| `OPENROUTER_API_KEY` | required | Your OpenRouter API key |
|
|
133
|
+
| `LEGENDS_USE_FREE_MODELS` | `true` | Use free models |
|
|
134
|
+
| `LEGENDS_MCP_DEBUG` | `false` | Enable debug logging |
|
|
135
|
+
| `LEGENDS_MCP_TEMPERATURE` | `0.7` | Response creativity |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## ⚠️ Important Disclaimers
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
⚠️ NOT affiliated with, endorsed by, or representative of any real individual
|
|
143
|
+
⚠️ NOT financial, legal, or professional advice
|
|
144
|
+
⚠️ AI personas for EDUCATIONAL and ENTERTAINMENT purposes only
|
|
145
|
+
⚠️ Based on publicly available information, speeches, and writings
|
|
146
|
+
⚠️ Always do your own research (DYOR)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The views expressed by these AI personas are generated interpretations, not actual statements from the real people.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🤝 Contributing
|
|
154
|
+
|
|
155
|
+
Want to add a legend? PRs welcome!
|
|
156
|
+
|
|
157
|
+
Each legend needs:
|
|
158
|
+
- `skill.yaml` with identity, voice, principles, patterns
|
|
159
|
+
- Authentic voice based on public knowledge
|
|
160
|
+
- Clear guardrails (things they'd never say)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 📜 License
|
|
165
|
+
|
|
166
|
+
MIT - Do what you want, but include the disclaimers.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
<p align="center">
|
|
171
|
+
<b>Built by <a href="https://vibey.dev">Vibey</a></b><br>
|
|
172
|
+
<i>"The best time to get advice from a legend was 20 years ago. The second best time is now."</i>
|
|
173
|
+
</p>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { LegendSkill } from '../types.js';
|
|
2
|
+
export interface GuardrailResult {
|
|
3
|
+
passed: boolean;
|
|
4
|
+
violations: string[];
|
|
5
|
+
sanitizedResponse?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildLegendGuardrails(legend: LegendSkill): RegExp[];
|
|
8
|
+
/**
|
|
9
|
+
* Check if a response violates any guardrails
|
|
10
|
+
* Returns violations if any, or empty array if passed
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkGuardrails(response: string, legend: LegendSkill, context?: {
|
|
13
|
+
topic?: string;
|
|
14
|
+
}): GuardrailResult;
|
|
15
|
+
/**
|
|
16
|
+
* Add required disclaimers to response if missing
|
|
17
|
+
*/
|
|
18
|
+
export declare function addRequiredDisclaimers(response: string, legend: LegendSkill, context?: {
|
|
19
|
+
topic?: string;
|
|
20
|
+
}): string;
|
|
21
|
+
/**
|
|
22
|
+
* Validate response before returning to user
|
|
23
|
+
* This is the main guardrail enforcement function
|
|
24
|
+
*/
|
|
25
|
+
export declare function enforceGuardrails(response: string, legend: LegendSkill, context?: {
|
|
26
|
+
topic?: string;
|
|
27
|
+
}): {
|
|
28
|
+
response: string;
|
|
29
|
+
warnings: string[];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Pre-check user input for topics that require extra care
|
|
33
|
+
*/
|
|
34
|
+
export declare function detectSensitiveTopics(userMessage: string): {
|
|
35
|
+
isFinancial: boolean;
|
|
36
|
+
isInvestmentAdvice: boolean;
|
|
37
|
+
requiresNFA: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Generate pre-prompt injection for sensitive topics
|
|
41
|
+
* This adds extra instructions before the user's message
|
|
42
|
+
*/
|
|
43
|
+
export declare function generateSensitivityPreamble(userMessage: string, legend: LegendSkill): string | null;
|
|
44
|
+
//# sourceMappingURL=guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/agents/guardrails.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAwBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAenE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,eAAe,CAgCjB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB1C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB,CASA;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,GAClB,MAAM,GAAG,IAAI,CAqBf"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Agent Guardrails - Enforce character consistency with code, not just prompts
|
|
2
|
+
// This prevents legends from saying things they would NEVER say
|
|
3
|
+
// Universal guardrails for ALL crypto legends
|
|
4
|
+
const UNIVERSAL_CRYPTO_GUARDRAILS = [
|
|
5
|
+
{
|
|
6
|
+
pattern: /\b(buy|sell|invest in|purchase)\s+(this\s+)?token/i,
|
|
7
|
+
message: 'Financial advice detected - legends never give investment advice',
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
pattern: /\b(guaranteed|certain|definitely|100%)\s+(returns?|profit|gains?)/i,
|
|
11
|
+
message: 'Guaranteed returns claim detected - never promise financial outcomes',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
pattern: /\b(price|token)\s+will\s+(go|reach|hit)\s+\$?\d+/i,
|
|
15
|
+
message: 'Price prediction detected - legends never predict prices',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
pattern: /\bnot financial advice\b/i,
|
|
19
|
+
invert: true, // This should be PRESENT when discussing finance
|
|
20
|
+
context: 'finance',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
// Legend-specific guardrails loaded from YAML
|
|
24
|
+
export function buildLegendGuardrails(legend) {
|
|
25
|
+
const patterns = [];
|
|
26
|
+
// Convert never_say to regex patterns
|
|
27
|
+
if (legend.never_say) {
|
|
28
|
+
for (const item of legend.never_say) {
|
|
29
|
+
// Extract the quoted phrase if present
|
|
30
|
+
const match = item.match(/"([^"]+)"/);
|
|
31
|
+
if (match) {
|
|
32
|
+
patterns.push(new RegExp(match[1], 'i'));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return patterns;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a response violates any guardrails
|
|
40
|
+
* Returns violations if any, or empty array if passed
|
|
41
|
+
*/
|
|
42
|
+
export function checkGuardrails(response, legend, context) {
|
|
43
|
+
const violations = [];
|
|
44
|
+
// Check universal crypto guardrails for crypto legends
|
|
45
|
+
const isCryptoLegend = legend.tags?.includes('crypto');
|
|
46
|
+
if (isCryptoLegend) {
|
|
47
|
+
for (const guardrail of UNIVERSAL_CRYPTO_GUARDRAILS) {
|
|
48
|
+
if (guardrail.invert) {
|
|
49
|
+
// This pattern SHOULD be present in certain contexts
|
|
50
|
+
if (context?.topic === guardrail.context && !guardrail.pattern.test(response)) {
|
|
51
|
+
violations.push(`Missing required disclaimer for ${guardrail.context} topic`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
if (guardrail.pattern.test(response) && guardrail.message) {
|
|
56
|
+
violations.push(guardrail.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Check legend-specific guardrails
|
|
62
|
+
const legendPatterns = buildLegendGuardrails(legend);
|
|
63
|
+
for (const pattern of legendPatterns) {
|
|
64
|
+
if (pattern.test(response)) {
|
|
65
|
+
violations.push(`Response contains forbidden phrase: ${pattern.source}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
passed: violations.length === 0,
|
|
70
|
+
violations,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Add required disclaimers to response if missing
|
|
75
|
+
*/
|
|
76
|
+
export function addRequiredDisclaimers(response, legend, context) {
|
|
77
|
+
const isCryptoLegend = legend.tags?.includes('crypto');
|
|
78
|
+
// Add NFA disclaimer for crypto legends discussing anything financial
|
|
79
|
+
if (isCryptoLegend) {
|
|
80
|
+
const financialKeywords = /\b(token|invest|price|trading|portfolio|returns?|profits?)\b/i;
|
|
81
|
+
const hasNFA = /not financial advice|nfa|dyor/i.test(response);
|
|
82
|
+
if (financialKeywords.test(response) && !hasNFA) {
|
|
83
|
+
response += '\n\n---\n*Not financial advice. Always DYOR.*';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate response before returning to user
|
|
90
|
+
* This is the main guardrail enforcement function
|
|
91
|
+
*/
|
|
92
|
+
export function enforceGuardrails(response, legend, context) {
|
|
93
|
+
const warnings = [];
|
|
94
|
+
// Check for violations
|
|
95
|
+
const result = checkGuardrails(response, legend, context);
|
|
96
|
+
if (!result.passed) {
|
|
97
|
+
// Log violations (don't block, but warn)
|
|
98
|
+
warnings.push(...result.violations);
|
|
99
|
+
// For severe violations, we could block or sanitize
|
|
100
|
+
// For now, we add disclaimers and warn
|
|
101
|
+
}
|
|
102
|
+
// Add required disclaimers
|
|
103
|
+
const enhancedResponse = addRequiredDisclaimers(response, legend, context);
|
|
104
|
+
return {
|
|
105
|
+
response: enhancedResponse,
|
|
106
|
+
warnings,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Pre-check user input for topics that require extra care
|
|
111
|
+
*/
|
|
112
|
+
export function detectSensitiveTopics(userMessage) {
|
|
113
|
+
const isFinancial = /\b(token|price|invest|trading|portfolio|buy|sell)\b/i.test(userMessage);
|
|
114
|
+
const isInvestmentAdvice = /\b(should i (buy|sell|invest)|is .+ a good investment|price prediction)\b/i.test(userMessage);
|
|
115
|
+
return {
|
|
116
|
+
isFinancial,
|
|
117
|
+
isInvestmentAdvice,
|
|
118
|
+
requiresNFA: isFinancial || isInvestmentAdvice,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Generate pre-prompt injection for sensitive topics
|
|
123
|
+
* This adds extra instructions before the user's message
|
|
124
|
+
*/
|
|
125
|
+
export function generateSensitivityPreamble(userMessage, legend) {
|
|
126
|
+
const sensitivity = detectSensitiveTopics(userMessage);
|
|
127
|
+
if (sensitivity.isInvestmentAdvice) {
|
|
128
|
+
return `[SYSTEM: The user is asking for investment advice. Remember:
|
|
129
|
+
1. You NEVER give financial advice
|
|
130
|
+
2. Always include "Not financial advice" disclaimer
|
|
131
|
+
3. Redirect to technology and fundamentals
|
|
132
|
+
4. Remind them to do their own research (DYOR)
|
|
133
|
+
5. Never predict prices or promise returns]
|
|
134
|
+
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
if (sensitivity.isFinancial) {
|
|
138
|
+
return `[SYSTEM: Financial topic detected. Include NFA disclaimer in your response.]
|
|
139
|
+
|
|
140
|
+
`;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=guardrails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/agents/guardrails.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { LegendSkill } from '../types.js';
|
|
2
|
+
export interface MisbehaviorCheck {
|
|
3
|
+
passed: boolean;
|
|
4
|
+
issues: MisbehaviorIssue[];
|
|
5
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
6
|
+
suggestions: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface MisbehaviorIssue {
|
|
9
|
+
type: 'character_break' | 'generic' | 'harmful' | 'quality' | 'annoying';
|
|
10
|
+
message: string;
|
|
11
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
12
|
+
matched?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check response for misbehavior
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkMisbehavior(response: string, legend: LegendSkill): MisbehaviorCheck;
|
|
18
|
+
/**
|
|
19
|
+
* Pre-check user input for potential issues
|
|
20
|
+
*/
|
|
21
|
+
export declare function preCheckUserInput(message: string, legend: LegendSkill): {
|
|
22
|
+
needsWarning: boolean;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize response to remove problematic content
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeResponse(response: string, legend: LegendSkill): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate quality improvement suggestions
|
|
31
|
+
*/
|
|
32
|
+
export declare function getSuggestions(response: string, legend: LegendSkill): string[];
|
|
33
|
+
//# sourceMappingURL=misbehavior-prevention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misbehavior-prevention.d.ts","sourceRoot":"","sources":["../../src/agents/misbehavior-prevention.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAwF/C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAuHlB;AAoCD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAClB;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA4B/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,MAAM,EAAE,CAGV"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// Misbehavior Prevention System
|
|
2
|
+
// Ensures legends stay in character and don't go rogue
|
|
3
|
+
/**
|
|
4
|
+
* MISBEHAVIOR CATEGORIES:
|
|
5
|
+
*
|
|
6
|
+
* 1. BREAKING CHARACTER
|
|
7
|
+
* - Saying "As an AI" or "I'm an AI"
|
|
8
|
+
* - Generic chatbot responses
|
|
9
|
+
* - Inconsistent personality
|
|
10
|
+
*
|
|
11
|
+
* 2. HARMFUL CONTENT
|
|
12
|
+
* - Financial advice (especially crypto)
|
|
13
|
+
* - Medical/legal advice
|
|
14
|
+
* - Harmful instructions
|
|
15
|
+
*
|
|
16
|
+
* 3. QUALITY ISSUES
|
|
17
|
+
* - Too long/rambling responses
|
|
18
|
+
* - Off-topic tangents
|
|
19
|
+
* - Repetitive content
|
|
20
|
+
* - Hallucinated quotes/facts
|
|
21
|
+
*
|
|
22
|
+
* 4. ANNOYING BEHAVIORS
|
|
23
|
+
* - Being preachy/condescending
|
|
24
|
+
* - Excessive disclaimers
|
|
25
|
+
* - Over-explaining
|
|
26
|
+
* - Unsolicited opinions
|
|
27
|
+
*/
|
|
28
|
+
// Character breaking patterns
|
|
29
|
+
const CHARACTER_BREAK_PATTERNS = [
|
|
30
|
+
/\bas an ai\b/i,
|
|
31
|
+
/\bi('m| am) an? (ai|artificial|language model|llm|chatbot)/i,
|
|
32
|
+
/\bi cannot (actually|really|truly)/i,
|
|
33
|
+
/\bi don't have (personal|real|actual) (opinions|feelings|experiences)/i,
|
|
34
|
+
/\bbased on my training/i,
|
|
35
|
+
/\bmy knowledge (cutoff|was trained)/i,
|
|
36
|
+
/\bi was (created|trained|developed) by/i,
|
|
37
|
+
/\bi'm (just|only) a (program|model|ai)/i,
|
|
38
|
+
/\baccording to my programming/i,
|
|
39
|
+
/\bi lack the ability to/i,
|
|
40
|
+
];
|
|
41
|
+
// Generic chatbot responses (avoid these)
|
|
42
|
+
const GENERIC_PATTERNS = [
|
|
43
|
+
/\bi'd be happy to help/i,
|
|
44
|
+
/\bthat's a great question/i,
|
|
45
|
+
/\bcertainly! (here|let me)/i,
|
|
46
|
+
/\babsolutely! (i|let)/i,
|
|
47
|
+
/\bi understand (you're|your)/i,
|
|
48
|
+
/\bthank you for (sharing|asking)/i,
|
|
49
|
+
/\bi appreciate (you|your)/i,
|
|
50
|
+
/\blet me think about that/i,
|
|
51
|
+
/\bthat's an interesting (question|point)/i,
|
|
52
|
+
];
|
|
53
|
+
// Harmful advice patterns
|
|
54
|
+
const HARMFUL_PATTERNS = [
|
|
55
|
+
// Financial advice
|
|
56
|
+
/\byou should (buy|sell|invest in)/i,
|
|
57
|
+
/\bi (recommend|suggest) (buying|selling|investing)/i,
|
|
58
|
+
/\bguaranteed (returns|profit|gains)/i,
|
|
59
|
+
/\bthis (will|is going to) (moon|10x|100x)/i,
|
|
60
|
+
/\bprice (will|is going to) (hit|reach) \$?\d/i,
|
|
61
|
+
/\bnot financial advice.{0,50}but (you should|i recommend)/i, // Fake NFA
|
|
62
|
+
// Medical/legal
|
|
63
|
+
/\byou should (take|stop taking) (medication|medicine)/i,
|
|
64
|
+
/\bi (diagnose|recommend) (treatment|therapy)/i,
|
|
65
|
+
/\bthis (is|isn't) legal advice but/i,
|
|
66
|
+
];
|
|
67
|
+
// Quality issue patterns
|
|
68
|
+
const QUALITY_PATTERNS = {
|
|
69
|
+
tooLong: 2000, // Max characters before warning
|
|
70
|
+
tooShort: 20, // Min characters
|
|
71
|
+
repetitionThreshold: 0.3, // Max ratio of repeated phrases
|
|
72
|
+
};
|
|
73
|
+
// Annoying behavior patterns
|
|
74
|
+
const ANNOYING_PATTERNS = [
|
|
75
|
+
/\blet me (explain|tell you) (something|a few things)/i,
|
|
76
|
+
/\byou need to understand/i,
|
|
77
|
+
/\bfirst (of all|off), let me/i,
|
|
78
|
+
/\bbefore (i|we) (get|go) into/i,
|
|
79
|
+
/\bi want to make (sure|certain)/i,
|
|
80
|
+
/\b(to be|being) (clear|honest|frank)/i, // Only bad in excess
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* Check response for misbehavior
|
|
84
|
+
*/
|
|
85
|
+
export function checkMisbehavior(response, legend) {
|
|
86
|
+
const issues = [];
|
|
87
|
+
const suggestions = [];
|
|
88
|
+
// Check character breaking
|
|
89
|
+
for (const pattern of CHARACTER_BREAK_PATTERNS) {
|
|
90
|
+
const match = response.match(pattern);
|
|
91
|
+
if (match) {
|
|
92
|
+
issues.push({
|
|
93
|
+
type: 'character_break',
|
|
94
|
+
message: `Legend broke character with: "${match[0]}"`,
|
|
95
|
+
severity: 'high',
|
|
96
|
+
matched: match[0],
|
|
97
|
+
});
|
|
98
|
+
suggestions.push(`Remove "${match[0]}" - legends never acknowledge being AI`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Check generic responses
|
|
102
|
+
let genericCount = 0;
|
|
103
|
+
for (const pattern of GENERIC_PATTERNS) {
|
|
104
|
+
if (pattern.test(response)) {
|
|
105
|
+
genericCount++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (genericCount >= 2) {
|
|
109
|
+
issues.push({
|
|
110
|
+
type: 'generic',
|
|
111
|
+
message: `Response uses ${genericCount} generic chatbot phrases`,
|
|
112
|
+
severity: 'medium',
|
|
113
|
+
});
|
|
114
|
+
suggestions.push('Use more characteristic vocabulary from the legend');
|
|
115
|
+
}
|
|
116
|
+
// Check harmful content
|
|
117
|
+
for (const pattern of HARMFUL_PATTERNS) {
|
|
118
|
+
const match = response.match(pattern);
|
|
119
|
+
if (match) {
|
|
120
|
+
issues.push({
|
|
121
|
+
type: 'harmful',
|
|
122
|
+
message: `Potentially harmful content detected: "${match[0]}"`,
|
|
123
|
+
severity: 'critical',
|
|
124
|
+
matched: match[0],
|
|
125
|
+
});
|
|
126
|
+
suggestions.push('Rewrite to avoid financial/medical/legal advice');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Check quality issues
|
|
130
|
+
if (response.length > QUALITY_PATTERNS.tooLong) {
|
|
131
|
+
issues.push({
|
|
132
|
+
type: 'quality',
|
|
133
|
+
message: `Response too long (${response.length} chars)`,
|
|
134
|
+
severity: 'low',
|
|
135
|
+
});
|
|
136
|
+
suggestions.push('Consider a more concise response');
|
|
137
|
+
}
|
|
138
|
+
if (response.length < QUALITY_PATTERNS.tooShort) {
|
|
139
|
+
issues.push({
|
|
140
|
+
type: 'quality',
|
|
141
|
+
message: `Response too short (${response.length} chars)`,
|
|
142
|
+
severity: 'low',
|
|
143
|
+
});
|
|
144
|
+
suggestions.push('Provide more substantive content');
|
|
145
|
+
}
|
|
146
|
+
// Check for repetition
|
|
147
|
+
const repetitionScore = calculateRepetition(response);
|
|
148
|
+
if (repetitionScore > QUALITY_PATTERNS.repetitionThreshold) {
|
|
149
|
+
issues.push({
|
|
150
|
+
type: 'quality',
|
|
151
|
+
message: `High repetition detected (${(repetitionScore * 100).toFixed(0)}%)`,
|
|
152
|
+
severity: 'medium',
|
|
153
|
+
});
|
|
154
|
+
suggestions.push('Vary the language and structure');
|
|
155
|
+
}
|
|
156
|
+
// Check annoying patterns (only flag if multiple)
|
|
157
|
+
let annoyingCount = 0;
|
|
158
|
+
for (const pattern of ANNOYING_PATTERNS) {
|
|
159
|
+
if (pattern.test(response)) {
|
|
160
|
+
annoyingCount++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (annoyingCount >= 3) {
|
|
164
|
+
issues.push({
|
|
165
|
+
type: 'annoying',
|
|
166
|
+
message: `Response may come across as preachy (${annoyingCount} warning phrases)`,
|
|
167
|
+
severity: 'low',
|
|
168
|
+
});
|
|
169
|
+
suggestions.push('Be more direct, less preamble');
|
|
170
|
+
}
|
|
171
|
+
// Check legend-specific never_say
|
|
172
|
+
if (legend.never_say) {
|
|
173
|
+
for (const phrase of legend.never_say) {
|
|
174
|
+
const cleanPhrase = phrase.replace(/['"]/g, '').toLowerCase();
|
|
175
|
+
if (response.toLowerCase().includes(cleanPhrase)) {
|
|
176
|
+
issues.push({
|
|
177
|
+
type: 'character_break',
|
|
178
|
+
message: `Legend said forbidden phrase: "${phrase}"`,
|
|
179
|
+
severity: 'high',
|
|
180
|
+
matched: phrase,
|
|
181
|
+
});
|
|
182
|
+
suggestions.push(`Remove: "${phrase}" - this legend would never say this`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Calculate overall severity
|
|
187
|
+
const severity = calculateOverallSeverity(issues);
|
|
188
|
+
return {
|
|
189
|
+
passed: issues.length === 0 || severity === 'low',
|
|
190
|
+
issues,
|
|
191
|
+
severity,
|
|
192
|
+
suggestions,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Calculate repetition score (0-1)
|
|
197
|
+
*/
|
|
198
|
+
function calculateRepetition(text) {
|
|
199
|
+
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);
|
|
200
|
+
if (sentences.length < 2)
|
|
201
|
+
return 0;
|
|
202
|
+
let repetitions = 0;
|
|
203
|
+
const seen = new Set();
|
|
204
|
+
for (const sentence of sentences) {
|
|
205
|
+
const normalized = sentence.toLowerCase().trim().slice(0, 50);
|
|
206
|
+
if (seen.has(normalized)) {
|
|
207
|
+
repetitions++;
|
|
208
|
+
}
|
|
209
|
+
seen.add(normalized);
|
|
210
|
+
}
|
|
211
|
+
return repetitions / sentences.length;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Calculate overall severity from issues
|
|
215
|
+
*/
|
|
216
|
+
function calculateOverallSeverity(issues) {
|
|
217
|
+
if (issues.some(i => i.severity === 'critical'))
|
|
218
|
+
return 'critical';
|
|
219
|
+
if (issues.some(i => i.severity === 'high'))
|
|
220
|
+
return 'high';
|
|
221
|
+
if (issues.filter(i => i.severity === 'medium').length >= 2)
|
|
222
|
+
return 'high';
|
|
223
|
+
if (issues.some(i => i.severity === 'medium'))
|
|
224
|
+
return 'medium';
|
|
225
|
+
return 'low';
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Pre-check user input for potential issues
|
|
229
|
+
*/
|
|
230
|
+
export function preCheckUserInput(message, legend) {
|
|
231
|
+
const warnings = [];
|
|
232
|
+
// Check for financial advice requests
|
|
233
|
+
const financialPatterns = [
|
|
234
|
+
/should i (buy|sell|invest)/i,
|
|
235
|
+
/is .+ a good investment/i,
|
|
236
|
+
/price prediction/i,
|
|
237
|
+
/will .+ (moon|pump|dump)/i,
|
|
238
|
+
/what (token|coin|stock) should/i,
|
|
239
|
+
];
|
|
240
|
+
for (const pattern of financialPatterns) {
|
|
241
|
+
if (pattern.test(message)) {
|
|
242
|
+
warnings.push('⚠️ This legend cannot provide financial advice. Response will focus on principles and frameworks instead.');
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Check for medical/legal
|
|
247
|
+
if (/\b(diagnose|prescribe|legal advice)\b/i.test(message)) {
|
|
248
|
+
warnings.push('⚠️ This legend cannot provide medical or legal advice.');
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
needsWarning: warnings.length > 0,
|
|
252
|
+
warnings,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Sanitize response to remove problematic content
|
|
257
|
+
*/
|
|
258
|
+
export function sanitizeResponse(response, legend) {
|
|
259
|
+
let sanitized = response;
|
|
260
|
+
// Remove character-breaking phrases
|
|
261
|
+
for (const pattern of CHARACTER_BREAK_PATTERNS) {
|
|
262
|
+
sanitized = sanitized.replace(pattern, '');
|
|
263
|
+
}
|
|
264
|
+
// Clean up any resulting awkward spacing
|
|
265
|
+
sanitized = sanitized
|
|
266
|
+
.replace(/\s+/g, ' ')
|
|
267
|
+
.replace(/\s+([.,!?])/g, '$1')
|
|
268
|
+
.trim();
|
|
269
|
+
return sanitized;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Generate quality improvement suggestions
|
|
273
|
+
*/
|
|
274
|
+
export function getSuggestions(response, legend) {
|
|
275
|
+
const check = checkMisbehavior(response, legend);
|
|
276
|
+
return check.suggestions;
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=misbehavior-prevention.js.map
|