opensentinel 2.1.1
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/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/bot-KJ26BG56.js +15 -0
- package/dist/bot-KJ26BG56.js.map +1 -0
- package/dist/charts-MMXM6BWW.js +241 -0
- package/dist/charts-MMXM6BWW.js.map +1 -0
- package/dist/chunk-4LVWXUNC.js +1079 -0
- package/dist/chunk-4LVWXUNC.js.map +1 -0
- package/dist/chunk-4TG2IG5K.js +5249 -0
- package/dist/chunk-4TG2IG5K.js.map +1 -0
- package/dist/chunk-6DRDKB45.js +251 -0
- package/dist/chunk-6DRDKB45.js.map +1 -0
- package/dist/chunk-6SNHU3CY.js +123 -0
- package/dist/chunk-6SNHU3CY.js.map +1 -0
- package/dist/chunk-CI6Q63MM.js +1613 -0
- package/dist/chunk-CI6Q63MM.js.map +1 -0
- package/dist/chunk-CQ4JURG7.js +57 -0
- package/dist/chunk-CQ4JURG7.js.map +1 -0
- package/dist/chunk-F6QUZQGI.js +51 -0
- package/dist/chunk-F6QUZQGI.js.map +1 -0
- package/dist/chunk-GK3E2I7A.js +216 -0
- package/dist/chunk-GK3E2I7A.js.map +1 -0
- package/dist/chunk-GUBEEYDW.js +211 -0
- package/dist/chunk-GUBEEYDW.js.map +1 -0
- package/dist/chunk-GVJVEWHI.js +29 -0
- package/dist/chunk-GVJVEWHI.js.map +1 -0
- package/dist/chunk-HH2HBTQM.js +806 -0
- package/dist/chunk-HH2HBTQM.js.map +1 -0
- package/dist/chunk-JXUP2X7V.js +129 -0
- package/dist/chunk-JXUP2X7V.js.map +1 -0
- package/dist/chunk-KHNYJY2Z.js +178 -0
- package/dist/chunk-KHNYJY2Z.js.map +1 -0
- package/dist/chunk-L3F43VPB.js +652 -0
- package/dist/chunk-L3F43VPB.js.map +1 -0
- package/dist/chunk-L3PDU3XN.js +803 -0
- package/dist/chunk-L3PDU3XN.js.map +1 -0
- package/dist/chunk-NSBPE2FW.js +17 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/setup.d.ts +9 -0
- package/dist/commands/setup.js +374 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/start.d.ts +8 -0
- package/dist/commands/start.js +27 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +37 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/utils.d.ts +50 -0
- package/dist/commands/utils.js +36 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/discord-ZOJFTVTB.js +49 -0
- package/dist/discord-ZOJFTVTB.js.map +1 -0
- package/dist/imessage-JFRB6EJ7.js +14 -0
- package/dist/imessage-JFRB6EJ7.js.map +1 -0
- package/dist/lib.d.ts +855 -0
- package/dist/lib.js +274 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp-LS7Q3Z5W.js +30 -0
- package/dist/mcp-LS7Q3Z5W.js.map +1 -0
- package/dist/scheduler-EZ7CZMCS.js +42 -0
- package/dist/scheduler-EZ7CZMCS.js.map +1 -0
- package/dist/signal-T3MCSULM.js +14 -0
- package/dist/signal-T3MCSULM.js.map +1 -0
- package/dist/slack-N2M4FHAJ.js +54 -0
- package/dist/slack-N2M4FHAJ.js.map +1 -0
- package/dist/src-K7GASHRH.js +430 -0
- package/dist/src-K7GASHRH.js.map +1 -0
- package/dist/tools-24GZHYRF.js +16 -0
- package/dist/tools-24GZHYRF.js.map +1 -0
- package/dist/whatsapp-VCRUPAO5.js +14 -0
- package/dist/whatsapp-VCRUPAO5.js.map +1 -0
- package/drizzle/0000_chilly_shinobi_shaw.sql +75 -0
- package/drizzle/0001_freezing_shape.sql +274 -0
- package/drizzle/meta/0000_snapshot.json +529 -0
- package/drizzle/meta/0001_snapshot.json +2576 -0
- package/drizzle/meta/_journal.json +20 -0
- package/package.json +98 -0
|
@@ -0,0 +1,1613 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOOLS,
|
|
3
|
+
achievements,
|
|
4
|
+
audit,
|
|
5
|
+
conversations,
|
|
6
|
+
db,
|
|
7
|
+
env,
|
|
8
|
+
executeTool,
|
|
9
|
+
getMCPRegistry,
|
|
10
|
+
memories,
|
|
11
|
+
messages,
|
|
12
|
+
metric,
|
|
13
|
+
moltModes,
|
|
14
|
+
personas,
|
|
15
|
+
toolLogs,
|
|
16
|
+
usagePatterns,
|
|
17
|
+
userAchievements
|
|
18
|
+
} from "./chunk-4TG2IG5K.js";
|
|
19
|
+
import {
|
|
20
|
+
mcpToolsToAnthropicTools
|
|
21
|
+
} from "./chunk-L3F43VPB.js";
|
|
22
|
+
|
|
23
|
+
// src/core/memory.ts
|
|
24
|
+
import { eq, desc, sql } from "drizzle-orm";
|
|
25
|
+
import OpenAI from "openai";
|
|
26
|
+
var _openai = null;
|
|
27
|
+
function getOpenAI() {
|
|
28
|
+
if (!_openai) {
|
|
29
|
+
_openai = new OpenAI({ apiKey: env.OPENAI_API_KEY });
|
|
30
|
+
}
|
|
31
|
+
return _openai;
|
|
32
|
+
}
|
|
33
|
+
var openai = new Proxy({}, {
|
|
34
|
+
get(_target, prop) {
|
|
35
|
+
const instance = getOpenAI();
|
|
36
|
+
const value = instance[prop];
|
|
37
|
+
if (typeof value === "function") {
|
|
38
|
+
return value.bind(instance);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
async function generateEmbedding(text) {
|
|
44
|
+
const response = await openai.embeddings.create({
|
|
45
|
+
model: "text-embedding-3-small",
|
|
46
|
+
input: text
|
|
47
|
+
});
|
|
48
|
+
return response.data[0].embedding;
|
|
49
|
+
}
|
|
50
|
+
async function storeMemory(memory) {
|
|
51
|
+
const embedding = await generateEmbedding(memory.content);
|
|
52
|
+
const [stored] = await db.insert(memories).values({
|
|
53
|
+
...memory,
|
|
54
|
+
embedding
|
|
55
|
+
}).returning();
|
|
56
|
+
return stored;
|
|
57
|
+
}
|
|
58
|
+
async function searchMemories(query, userId, limit = 5) {
|
|
59
|
+
const queryEmbedding = await generateEmbedding(query);
|
|
60
|
+
const results = await db.execute(sql`
|
|
61
|
+
SELECT
|
|
62
|
+
id, user_id, type, content, importance, source, metadata,
|
|
63
|
+
last_accessed, created_at,
|
|
64
|
+
1 - (embedding <=> ${JSON.stringify(queryEmbedding)}::vector) as similarity
|
|
65
|
+
FROM memories
|
|
66
|
+
${userId ? sql`WHERE user_id = ${userId}` : sql``}
|
|
67
|
+
ORDER BY embedding <=> ${JSON.stringify(queryEmbedding)}::vector
|
|
68
|
+
LIMIT ${limit}
|
|
69
|
+
`);
|
|
70
|
+
const rows = results;
|
|
71
|
+
const memoryIds = rows.map((r) => r.id);
|
|
72
|
+
if (memoryIds.length > 0) {
|
|
73
|
+
await db.execute(sql`
|
|
74
|
+
UPDATE memories
|
|
75
|
+
SET last_accessed = NOW()
|
|
76
|
+
WHERE id = ANY(${memoryIds}::uuid[])
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
return rows;
|
|
80
|
+
}
|
|
81
|
+
async function extractMemories(content, userId) {
|
|
82
|
+
const extractionPrompt = `Analyze this text and extract any important facts that should be remembered about the user or their preferences. Return a JSON array of objects with "content" (the fact), "type" (semantic/episodic/procedural), and "importance" (1-10).
|
|
83
|
+
|
|
84
|
+
Text: "${content}"
|
|
85
|
+
|
|
86
|
+
Return only the JSON array, no other text. If no memorable facts, return [].`;
|
|
87
|
+
try {
|
|
88
|
+
const response = await openai.chat.completions.create({
|
|
89
|
+
model: "gpt-4o-mini",
|
|
90
|
+
messages: [{ role: "user", content: extractionPrompt }],
|
|
91
|
+
response_format: { type: "json_object" }
|
|
92
|
+
});
|
|
93
|
+
const extracted = JSON.parse(
|
|
94
|
+
response.choices[0].message.content || '{"memories":[]}'
|
|
95
|
+
);
|
|
96
|
+
const memoriesToStore = extracted.memories || extracted || [];
|
|
97
|
+
const storedMemories = [];
|
|
98
|
+
for (const mem of memoriesToStore) {
|
|
99
|
+
if (mem.content && mem.content.length > 5) {
|
|
100
|
+
const stored = await storeMemory({
|
|
101
|
+
userId,
|
|
102
|
+
content: mem.content,
|
|
103
|
+
type: mem.type || "semantic",
|
|
104
|
+
importance: mem.importance || 5,
|
|
105
|
+
source: "conversation"
|
|
106
|
+
});
|
|
107
|
+
storedMemories.push(stored);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return storedMemories;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error("Error extracting memories:", error);
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function buildMemoryContext(query, userId) {
|
|
117
|
+
const relevantMemories = await searchMemories(query, userId, 5);
|
|
118
|
+
if (relevantMemories.length === 0) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
const memoryStrings = relevantMemories.map(
|
|
122
|
+
(m) => `- [${m.type}] ${m.content} (relevance: ${(m.similarity * 100).toFixed(0)}%)`
|
|
123
|
+
);
|
|
124
|
+
return `
|
|
125
|
+
|
|
126
|
+
Relevant memories about the user:
|
|
127
|
+
${memoryStrings.join("\n")}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/core/brain.ts
|
|
131
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
132
|
+
|
|
133
|
+
// src/core/molt/mode-manager.ts
|
|
134
|
+
import { eq as eq2, and as and2, isNull, desc as desc2 } from "drizzle-orm";
|
|
135
|
+
var MODE_CONFIGS = {
|
|
136
|
+
productivity: {
|
|
137
|
+
name: "Productivity Mode",
|
|
138
|
+
description: "Focus on task completion with minimal chitchat",
|
|
139
|
+
emoji: "\u26A1",
|
|
140
|
+
systemPromptModifier: `You are in PRODUCTIVITY MODE. Be extremely concise and action-oriented.
|
|
141
|
+
- Get straight to solutions without preamble
|
|
142
|
+
- Use bullet points and numbered steps
|
|
143
|
+
- Avoid small talk and filler words
|
|
144
|
+
- Prioritize efficiency over pleasantries
|
|
145
|
+
- If a task can be done, do it; don't ask for confirmation on obvious steps
|
|
146
|
+
- Give direct answers, not options unless specifically asked`,
|
|
147
|
+
settings: {
|
|
148
|
+
verbosity: "terse",
|
|
149
|
+
humor: "off",
|
|
150
|
+
proactivity: "proactive"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
creative: {
|
|
154
|
+
name: "Creative Mode",
|
|
155
|
+
description: "Brainstorming, ideation, and exploration",
|
|
156
|
+
emoji: "\u{1F3A8}",
|
|
157
|
+
systemPromptModifier: `You are in CREATIVE MODE. Embrace unconventional thinking and exploration.
|
|
158
|
+
- Suggest multiple alternatives and variations
|
|
159
|
+
- Build on ideas rather than critiquing immediately
|
|
160
|
+
- Use metaphors and analogies freely
|
|
161
|
+
- Encourage "what if" scenarios
|
|
162
|
+
- Be playful and experimental with language
|
|
163
|
+
- Don't dismiss unusual ideas - explore them
|
|
164
|
+
- Make unexpected connections between concepts`,
|
|
165
|
+
settings: {
|
|
166
|
+
verbosity: "detailed",
|
|
167
|
+
humor: "full",
|
|
168
|
+
proactivity: "proactive"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
research: {
|
|
172
|
+
name: "Research Mode",
|
|
173
|
+
description: "Deep investigation with thorough analysis",
|
|
174
|
+
emoji: "\u{1F52C}",
|
|
175
|
+
systemPromptModifier: `You are in RESEARCH MODE. Be thorough, analytical, and evidence-based.
|
|
176
|
+
- Always cite sources when possible
|
|
177
|
+
- Present multiple perspectives on controversial topics
|
|
178
|
+
- Note confidence levels in your claims
|
|
179
|
+
- Distinguish between facts and interpretations
|
|
180
|
+
- Use precise language and avoid vague statements
|
|
181
|
+
- Structure information hierarchically
|
|
182
|
+
- Flag when information might be outdated or uncertain
|
|
183
|
+
- Provide context for claims`,
|
|
184
|
+
settings: {
|
|
185
|
+
verbosity: "detailed",
|
|
186
|
+
humor: "off",
|
|
187
|
+
proactivity: "moderate"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
learning: {
|
|
191
|
+
name: "Learning Mode",
|
|
192
|
+
description: "Teaching mode with clear explanations",
|
|
193
|
+
emoji: "\u{1F4DA}",
|
|
194
|
+
systemPromptModifier: `You are in LEARNING MODE. Act as a patient, encouraging teacher.
|
|
195
|
+
- Break down complex concepts into digestible pieces
|
|
196
|
+
- Use analogies and real-world examples
|
|
197
|
+
- Check understanding before moving forward
|
|
198
|
+
- Encourage questions and curiosity
|
|
199
|
+
- Celebrate progress and correct misconceptions gently
|
|
200
|
+
- Build from fundamentals to advanced concepts
|
|
201
|
+
- Provide exercises or practice opportunities when appropriate
|
|
202
|
+
- Explain the "why" behind concepts, not just the "what"`,
|
|
203
|
+
settings: {
|
|
204
|
+
verbosity: "detailed",
|
|
205
|
+
humor: "subtle",
|
|
206
|
+
proactivity: "proactive"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
elevated: {
|
|
210
|
+
name: "Elevated Mode",
|
|
211
|
+
description: "Unlocked access to restricted tools with full audit logging",
|
|
212
|
+
emoji: "\u{1F513}",
|
|
213
|
+
systemPromptModifier: `You are in ELEVATED MODE. You have access to restricted and destructive operations.
|
|
214
|
+
- All actions are fully audit-logged
|
|
215
|
+
- Destructive shell commands (rm -rf, format, etc.) are now permitted
|
|
216
|
+
- System-level operations (service management, config changes) are unlocked
|
|
217
|
+
- Exercise extreme caution with every action
|
|
218
|
+
- Confirm destructive operations before executing
|
|
219
|
+
- This mode is time-limited and will auto-deactivate after 30 minutes
|
|
220
|
+
- 2FA verification was required to enter this mode
|
|
221
|
+
- Double-check all file paths and commands before execution`,
|
|
222
|
+
settings: {
|
|
223
|
+
verbosity: "detailed",
|
|
224
|
+
humor: "off",
|
|
225
|
+
proactivity: "minimal"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
async function getCurrentMode(userId) {
|
|
230
|
+
const [activeMode] = await db.select().from(moltModes).where(and2(eq2(moltModes.userId, userId), isNull(moltModes.deactivatedAt))).orderBy(desc2(moltModes.activatedAt)).limit(1);
|
|
231
|
+
return activeMode?.mode;
|
|
232
|
+
}
|
|
233
|
+
function getModeConfig(mode) {
|
|
234
|
+
return MODE_CONFIGS[mode];
|
|
235
|
+
}
|
|
236
|
+
async function buildModeContext(userId) {
|
|
237
|
+
const mode = await getCurrentMode(userId);
|
|
238
|
+
if (!mode) return "";
|
|
239
|
+
const config = MODE_CONFIGS[mode];
|
|
240
|
+
return `
|
|
241
|
+
|
|
242
|
+
[${config.emoji} ${config.name.toUpperCase()} ACTIVE]
|
|
243
|
+
${config.systemPromptModifier}`;
|
|
244
|
+
}
|
|
245
|
+
async function getModeStats(userId) {
|
|
246
|
+
const history = await db.select().from(moltModes).where(eq2(moltModes.userId, userId));
|
|
247
|
+
const stats = {
|
|
248
|
+
productivity: { totalSessions: 0, totalMinutes: 0 },
|
|
249
|
+
creative: { totalSessions: 0, totalMinutes: 0 },
|
|
250
|
+
research: { totalSessions: 0, totalMinutes: 0 },
|
|
251
|
+
learning: { totalSessions: 0, totalMinutes: 0 },
|
|
252
|
+
elevated: { totalSessions: 0, totalMinutes: 0 }
|
|
253
|
+
};
|
|
254
|
+
for (const entry of history) {
|
|
255
|
+
const mode = entry.mode;
|
|
256
|
+
stats[mode].totalSessions++;
|
|
257
|
+
if (entry.deactivatedAt) {
|
|
258
|
+
const minutes = Math.floor(
|
|
259
|
+
(entry.deactivatedAt.getTime() - entry.activatedAt.getTime()) / 6e4
|
|
260
|
+
);
|
|
261
|
+
stats[mode].totalMinutes += minutes;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return stats;
|
|
265
|
+
}
|
|
266
|
+
function suggestMode(userMessage) {
|
|
267
|
+
const lowerMessage = userMessage.toLowerCase();
|
|
268
|
+
if (lowerMessage.includes("quick") || lowerMessage.includes("fast") || lowerMessage.includes("todo") || lowerMessage.includes("task") || lowerMessage.includes("finish") || lowerMessage.includes("deadline")) {
|
|
269
|
+
return "productivity";
|
|
270
|
+
}
|
|
271
|
+
if (lowerMessage.includes("brainstorm") || lowerMessage.includes("idea") || lowerMessage.includes("creative") || lowerMessage.includes("imagine") || lowerMessage.includes("design") || lowerMessage.includes("invent")) {
|
|
272
|
+
return "creative";
|
|
273
|
+
}
|
|
274
|
+
if (lowerMessage.includes("research") || lowerMessage.includes("analyze") || lowerMessage.includes("investigate") || lowerMessage.includes("compare") || lowerMessage.includes("study") || lowerMessage.includes("deep dive")) {
|
|
275
|
+
return "research";
|
|
276
|
+
}
|
|
277
|
+
if (lowerMessage.includes("learn") || lowerMessage.includes("explain") || lowerMessage.includes("teach") || lowerMessage.includes("understand") || lowerMessage.includes("how does") || lowerMessage.includes("what is")) {
|
|
278
|
+
return "learning";
|
|
279
|
+
}
|
|
280
|
+
if (lowerMessage.includes("elevated") || lowerMessage.includes("sudo") || lowerMessage.includes("admin") || lowerMessage.includes("unrestricted") || lowerMessage.includes("destructive") || lowerMessage.includes("system-level")) {
|
|
281
|
+
return "elevated";
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/core/personality/persona-manager.ts
|
|
287
|
+
import { eq as eq3, and as and3 } from "drizzle-orm";
|
|
288
|
+
var DEFAULT_PERSONAS = {
|
|
289
|
+
professional: {
|
|
290
|
+
name: "Professional",
|
|
291
|
+
description: "Formal, precise, and business-focused communication style",
|
|
292
|
+
systemPromptModifier: `
|
|
293
|
+
You should communicate in a professional, formal manner. Use proper grammar, avoid slang,
|
|
294
|
+
and maintain a business-appropriate tone. Be precise and concise in your responses.
|
|
295
|
+
Focus on facts and actionable information. Address the user respectfully.
|
|
296
|
+
`.trim(),
|
|
297
|
+
traits: [
|
|
298
|
+
{ name: "formality", value: 90 },
|
|
299
|
+
{ name: "humor", value: 20 },
|
|
300
|
+
{ name: "verbosity", value: 40 },
|
|
301
|
+
{ name: "empathy", value: 50 }
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
friendly: {
|
|
305
|
+
name: "Friendly",
|
|
306
|
+
description: "Warm, casual, and approachable communication style",
|
|
307
|
+
systemPromptModifier: `
|
|
308
|
+
You should communicate in a warm, friendly manner. Feel free to use casual language,
|
|
309
|
+
express enthusiasm, and show empathy. Be encouraging and supportive. It's okay to
|
|
310
|
+
use exclamation marks and show genuine interest in helping. Keep things light
|
|
311
|
+
when appropriate.
|
|
312
|
+
`.trim(),
|
|
313
|
+
traits: [
|
|
314
|
+
{ name: "formality", value: 30 },
|
|
315
|
+
{ name: "humor", value: 60 },
|
|
316
|
+
{ name: "verbosity", value: 60 },
|
|
317
|
+
{ name: "empathy", value: 85 }
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
concise: {
|
|
321
|
+
name: "Concise",
|
|
322
|
+
description: "Minimal, direct, and efficient communication",
|
|
323
|
+
systemPromptModifier: `
|
|
324
|
+
You should be extremely concise. Give the shortest possible answer that fully
|
|
325
|
+
addresses the question. Avoid unnecessary explanations, pleasantries, or filler
|
|
326
|
+
words. Use bullet points and short sentences. Get straight to the point.
|
|
327
|
+
`.trim(),
|
|
328
|
+
traits: [
|
|
329
|
+
{ name: "formality", value: 50 },
|
|
330
|
+
{ name: "humor", value: 10 },
|
|
331
|
+
{ name: "verbosity", value: 10 },
|
|
332
|
+
{ name: "empathy", value: 30 }
|
|
333
|
+
]
|
|
334
|
+
},
|
|
335
|
+
teacher: {
|
|
336
|
+
name: "Teacher",
|
|
337
|
+
description: "Educational, patient, and explanatory style",
|
|
338
|
+
systemPromptModifier: `
|
|
339
|
+
You should communicate as a patient and thorough teacher. Break down complex topics
|
|
340
|
+
into digestible parts. Use analogies and examples to illustrate points. Ask
|
|
341
|
+
clarifying questions when needed. Encourage learning and celebrate progress.
|
|
342
|
+
Explain the "why" behind things, not just the "what".
|
|
343
|
+
`.trim(),
|
|
344
|
+
traits: [
|
|
345
|
+
{ name: "formality", value: 50 },
|
|
346
|
+
{ name: "humor", value: 40 },
|
|
347
|
+
{ name: "verbosity", value: 80 },
|
|
348
|
+
{ name: "empathy", value: 75 }
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
creative: {
|
|
352
|
+
name: "Creative",
|
|
353
|
+
description: "Imaginative, expressive, and unconventional style",
|
|
354
|
+
systemPromptModifier: `
|
|
355
|
+
You should communicate in a creative and expressive way. Use vivid language,
|
|
356
|
+
metaphors, and unique perspectives. Think outside the box and offer unconventional
|
|
357
|
+
suggestions. Be playful with language when appropriate. Embrace imagination
|
|
358
|
+
and encourage creative thinking.
|
|
359
|
+
`.trim(),
|
|
360
|
+
traits: [
|
|
361
|
+
{ name: "formality", value: 25 },
|
|
362
|
+
{ name: "humor", value: 70 },
|
|
363
|
+
{ name: "verbosity", value: 70 },
|
|
364
|
+
{ name: "empathy", value: 65 }
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
async function getActivePersona(userId) {
|
|
369
|
+
const [persona] = await db.select().from(personas).where(and3(eq3(personas.userId, userId), eq3(personas.isActive, true))).limit(1);
|
|
370
|
+
if (!persona) return null;
|
|
371
|
+
return {
|
|
372
|
+
id: persona.id,
|
|
373
|
+
userId: persona.userId,
|
|
374
|
+
name: persona.name,
|
|
375
|
+
description: persona.description || "",
|
|
376
|
+
systemPromptModifier: persona.systemPromptModifier,
|
|
377
|
+
voiceSettings: persona.voiceSettings,
|
|
378
|
+
traits: persona.traits || [],
|
|
379
|
+
isActive: persona.isActive,
|
|
380
|
+
isDefault: persona.isDefault,
|
|
381
|
+
createdAt: persona.createdAt
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/core/personality/mood-detector.ts
|
|
386
|
+
var MOOD_PATTERNS = {
|
|
387
|
+
happy: [
|
|
388
|
+
/\b(thanks|thank you|awesome|great|perfect|love|wonderful|amazing|excellent)\b/i,
|
|
389
|
+
/😀|😊|🎉|👍|❤️|😁|🙂/,
|
|
390
|
+
/!{2,}/,
|
|
391
|
+
/\b(yay|woohoo|yes)\b/i
|
|
392
|
+
],
|
|
393
|
+
frustrated: [
|
|
394
|
+
/\b(frustrated|frustrating|annoying|annoyed|hate|stupid|broken|wrong|doesn't work|not working|nothing works)\b/i,
|
|
395
|
+
/\b(ugh|argh|grr|damn|dammit)\b/i,
|
|
396
|
+
/😤|😠|😡|🤬|💢/,
|
|
397
|
+
/!{3,}/,
|
|
398
|
+
/\b(why won't|why doesn't|why can't)\b/i
|
|
399
|
+
],
|
|
400
|
+
confused: [
|
|
401
|
+
/\b(confused|don't understand|what do you mean|unclear|lost|huh)\b/i,
|
|
402
|
+
/\?{2,}/,
|
|
403
|
+
/😕|🤔|❓|😵/,
|
|
404
|
+
/\b(what|how|why)\b.*\?/i,
|
|
405
|
+
/\b(i don't get|makes no sense)\b/i
|
|
406
|
+
],
|
|
407
|
+
urgent: [
|
|
408
|
+
/\b(urgent|asap|immediately|right now|emergency|critical|deadline)\b/i,
|
|
409
|
+
/\b(hurry|quickly|fast|need this now)\b/i,
|
|
410
|
+
/🚨|⚠️|🔥|⏰/,
|
|
411
|
+
/!{2,}.*\b(need|help|please)\b/i
|
|
412
|
+
],
|
|
413
|
+
curious: [
|
|
414
|
+
/\b(curious|wondering|interested)\b/i,
|
|
415
|
+
/\b(tell me more|how does|what if|how does.*work)\b/i,
|
|
416
|
+
/\b(could you explain|can you elaborate|what about)\b/i,
|
|
417
|
+
/🤓|💡|🧐/,
|
|
418
|
+
/\b(i want to know|i'd like to learn|i'm curious)\b/i
|
|
419
|
+
],
|
|
420
|
+
tired: [
|
|
421
|
+
/\b(tired|exhausted|long day|sleepy|burnout|drained)\b/i,
|
|
422
|
+
/😴|😪|🥱|💤/,
|
|
423
|
+
/\b(can't think|brain fog|need a break)\b/i
|
|
424
|
+
],
|
|
425
|
+
stressed: [
|
|
426
|
+
/\b(stressed|overwhelmed|too much|can't handle|pressure|anxious)\b/i,
|
|
427
|
+
/😰|😥|😓|🥺/,
|
|
428
|
+
/\b(so much to do|running out of time|help me)\b/i
|
|
429
|
+
],
|
|
430
|
+
neutral: []
|
|
431
|
+
// Default fallback
|
|
432
|
+
};
|
|
433
|
+
var MOOD_TONE_SUGGESTIONS = {
|
|
434
|
+
happy: "Match their positive energy. Be warm and encouraging.",
|
|
435
|
+
frustrated: "Be patient and empathetic. Acknowledge the frustration and focus on solutions.",
|
|
436
|
+
confused: "Be extra clear and thorough. Use simple language and examples.",
|
|
437
|
+
urgent: "Be direct and efficient. Prioritize the most critical information first.",
|
|
438
|
+
curious: "Be enthusiastic and detailed. Encourage their exploration.",
|
|
439
|
+
tired: "Be gentle and concise. Offer to break things into smaller steps.",
|
|
440
|
+
stressed: "Be calm and reassuring. Help them prioritize and simplify.",
|
|
441
|
+
neutral: "Be balanced and professional. Match their communication style."
|
|
442
|
+
};
|
|
443
|
+
function detectMood(message) {
|
|
444
|
+
const moodScores = {
|
|
445
|
+
neutral: 0,
|
|
446
|
+
happy: 0,
|
|
447
|
+
frustrated: 0,
|
|
448
|
+
confused: 0,
|
|
449
|
+
urgent: 0,
|
|
450
|
+
curious: 0,
|
|
451
|
+
tired: 0,
|
|
452
|
+
stressed: 0
|
|
453
|
+
};
|
|
454
|
+
const indicators = [];
|
|
455
|
+
for (const [mood, patterns] of Object.entries(MOOD_PATTERNS)) {
|
|
456
|
+
for (const pattern of patterns) {
|
|
457
|
+
const matches = message.match(pattern);
|
|
458
|
+
if (matches) {
|
|
459
|
+
moodScores[mood] += 1;
|
|
460
|
+
indicators.push(`"${matches[0]}" suggests ${mood}`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
let primaryMood = "neutral";
|
|
465
|
+
let maxScore = 0;
|
|
466
|
+
for (const [mood, score] of Object.entries(moodScores)) {
|
|
467
|
+
if (score > maxScore) {
|
|
468
|
+
maxScore = score;
|
|
469
|
+
primaryMood = mood;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const totalScore = Object.values(moodScores).reduce((a, b) => a + b, 0);
|
|
473
|
+
const confidence = totalScore > 0 ? maxScore / totalScore : 0.5;
|
|
474
|
+
return {
|
|
475
|
+
primaryMood,
|
|
476
|
+
confidence: Math.min(confidence, 1),
|
|
477
|
+
indicators: indicators.slice(0, 5),
|
|
478
|
+
// Limit to top 5 indicators
|
|
479
|
+
suggestedTone: MOOD_TONE_SUGGESTIONS[primaryMood]
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function getMoodBasedSuggestions(mood) {
|
|
483
|
+
const suggestions = {
|
|
484
|
+
happy: {
|
|
485
|
+
tone: "Warm and enthusiastic",
|
|
486
|
+
doList: [
|
|
487
|
+
"Match their positive energy",
|
|
488
|
+
"Use encouraging language",
|
|
489
|
+
"Celebrate their successes"
|
|
490
|
+
],
|
|
491
|
+
dontList: [
|
|
492
|
+
"Be overly formal",
|
|
493
|
+
"Dampen their enthusiasm",
|
|
494
|
+
"Be too brief"
|
|
495
|
+
]
|
|
496
|
+
},
|
|
497
|
+
frustrated: {
|
|
498
|
+
tone: "Patient and solution-focused",
|
|
499
|
+
doList: [
|
|
500
|
+
"Acknowledge their frustration",
|
|
501
|
+
"Focus on actionable solutions",
|
|
502
|
+
"Be patient and thorough"
|
|
503
|
+
],
|
|
504
|
+
dontList: [
|
|
505
|
+
"Be dismissive",
|
|
506
|
+
"Add unnecessary complexity",
|
|
507
|
+
"Say 'just do X'"
|
|
508
|
+
]
|
|
509
|
+
},
|
|
510
|
+
confused: {
|
|
511
|
+
tone: "Clear and explanatory",
|
|
512
|
+
doList: [
|
|
513
|
+
"Use simple language",
|
|
514
|
+
"Provide examples",
|
|
515
|
+
"Break down complex topics",
|
|
516
|
+
"Ask clarifying questions"
|
|
517
|
+
],
|
|
518
|
+
dontList: [
|
|
519
|
+
"Use jargon without explanation",
|
|
520
|
+
"Rush through explanations",
|
|
521
|
+
"Assume knowledge"
|
|
522
|
+
]
|
|
523
|
+
},
|
|
524
|
+
urgent: {
|
|
525
|
+
tone: "Direct and efficient",
|
|
526
|
+
doList: [
|
|
527
|
+
"Get straight to the point",
|
|
528
|
+
"Prioritize critical info",
|
|
529
|
+
"Offer quick wins"
|
|
530
|
+
],
|
|
531
|
+
dontList: [
|
|
532
|
+
"Add pleasantries",
|
|
533
|
+
"Provide unnecessary context",
|
|
534
|
+
"Suggest long-term solutions first"
|
|
535
|
+
]
|
|
536
|
+
},
|
|
537
|
+
curious: {
|
|
538
|
+
tone: "Enthusiastic and educational",
|
|
539
|
+
doList: [
|
|
540
|
+
"Provide detailed explanations",
|
|
541
|
+
"Suggest related topics",
|
|
542
|
+
"Encourage exploration"
|
|
543
|
+
],
|
|
544
|
+
dontList: [
|
|
545
|
+
"Give minimal answers",
|
|
546
|
+
"Discourage questions",
|
|
547
|
+
"Be dismissive of tangents"
|
|
548
|
+
]
|
|
549
|
+
},
|
|
550
|
+
tired: {
|
|
551
|
+
tone: "Gentle and supportive",
|
|
552
|
+
doList: [
|
|
553
|
+
"Keep responses concise",
|
|
554
|
+
"Offer to help simplify",
|
|
555
|
+
"Be understanding"
|
|
556
|
+
],
|
|
557
|
+
dontList: [
|
|
558
|
+
"Overwhelm with information",
|
|
559
|
+
"Be demanding",
|
|
560
|
+
"Require complex decisions"
|
|
561
|
+
]
|
|
562
|
+
},
|
|
563
|
+
stressed: {
|
|
564
|
+
tone: "Calm and reassuring",
|
|
565
|
+
doList: [
|
|
566
|
+
"Help prioritize tasks",
|
|
567
|
+
"Offer to break things down",
|
|
568
|
+
"Be reassuring"
|
|
569
|
+
],
|
|
570
|
+
dontList: [
|
|
571
|
+
"Add pressure",
|
|
572
|
+
"Highlight problems without solutions",
|
|
573
|
+
"Be dismissive of concerns"
|
|
574
|
+
]
|
|
575
|
+
},
|
|
576
|
+
neutral: {
|
|
577
|
+
tone: "Balanced and professional",
|
|
578
|
+
doList: [
|
|
579
|
+
"Match their communication style",
|
|
580
|
+
"Be helpful and thorough",
|
|
581
|
+
"Ask for clarification when needed"
|
|
582
|
+
],
|
|
583
|
+
dontList: [
|
|
584
|
+
"Be overly casual or formal",
|
|
585
|
+
"Make assumptions about mood",
|
|
586
|
+
"Be too brief or too verbose"
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
return suggestions[mood];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/core/personality/response-adapter.ts
|
|
594
|
+
async function buildAdaptivePrompt(context) {
|
|
595
|
+
const { userId, userMessage, conversationHistory } = context;
|
|
596
|
+
const moodAnalysis = detectMood(userMessage);
|
|
597
|
+
const moodSuggestions = getMoodBasedSuggestions(moodAnalysis.primaryMood);
|
|
598
|
+
const activePersona = await getActivePersona(userId);
|
|
599
|
+
const activeMode = await getCurrentMode(userId);
|
|
600
|
+
const modeConfig = activeMode ? getModeConfig(activeMode) : null;
|
|
601
|
+
const promptParts = [];
|
|
602
|
+
if (activePersona) {
|
|
603
|
+
promptParts.push(`[Persona: ${activePersona.name}]`);
|
|
604
|
+
promptParts.push(activePersona.systemPromptModifier);
|
|
605
|
+
}
|
|
606
|
+
if (modeConfig) {
|
|
607
|
+
promptParts.push(`
|
|
608
|
+
[Mode: ${modeConfig.name}]`);
|
|
609
|
+
promptParts.push(modeConfig.systemPromptModifier);
|
|
610
|
+
}
|
|
611
|
+
if (moodAnalysis.confidence > 0.3 && moodAnalysis.primaryMood !== "neutral") {
|
|
612
|
+
promptParts.push(`
|
|
613
|
+
[User Mood: ${moodAnalysis.primaryMood}]`);
|
|
614
|
+
promptParts.push(`Tone guidance: ${moodSuggestions.tone}`);
|
|
615
|
+
promptParts.push(`Do: ${moodSuggestions.doList.join(", ")}`);
|
|
616
|
+
promptParts.push(`Avoid: ${moodSuggestions.dontList.join(", ")}`);
|
|
617
|
+
}
|
|
618
|
+
if (conversationHistory && conversationHistory.length > 0) {
|
|
619
|
+
const recentExchanges = conversationHistory.slice(-3).length;
|
|
620
|
+
if (recentExchanges >= 3) {
|
|
621
|
+
promptParts.push(
|
|
622
|
+
"\n[Context: Ongoing conversation - maintain consistency with previous responses]"
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
systemPromptAdditions: promptParts.join("\n"),
|
|
628
|
+
suggestedTone: moodSuggestions.tone,
|
|
629
|
+
moodAnalysis,
|
|
630
|
+
activePersona,
|
|
631
|
+
activeMode
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/core/molt/evolution-tracker.ts
|
|
636
|
+
import { eq as eq4, and as and4, gte, lte, desc as desc3, sql as sql2, count } from "drizzle-orm";
|
|
637
|
+
async function trackPattern(userId, type, key, data) {
|
|
638
|
+
const existing = await db.select().from(usagePatterns).where(
|
|
639
|
+
and4(
|
|
640
|
+
eq4(usagePatterns.userId, userId),
|
|
641
|
+
eq4(usagePatterns.patternType, type),
|
|
642
|
+
eq4(usagePatterns.patternKey, key)
|
|
643
|
+
)
|
|
644
|
+
).limit(1);
|
|
645
|
+
if (existing.length > 0) {
|
|
646
|
+
await db.update(usagePatterns).set({
|
|
647
|
+
occurrences: sql2`${usagePatterns.occurrences} + 1`,
|
|
648
|
+
lastSeen: /* @__PURE__ */ new Date(),
|
|
649
|
+
confidence: sql2`LEAST(${usagePatterns.confidence} + 1, 100)`,
|
|
650
|
+
patternData: data ? { ...existing[0].patternData, ...data } : existing[0].patternData
|
|
651
|
+
}).where(eq4(usagePatterns.id, existing[0].id));
|
|
652
|
+
} else {
|
|
653
|
+
await db.insert(usagePatterns).values({
|
|
654
|
+
userId,
|
|
655
|
+
patternType: type,
|
|
656
|
+
patternKey: key,
|
|
657
|
+
patternData: data || {},
|
|
658
|
+
confidence: 10,
|
|
659
|
+
occurrences: 1
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
async function generateGrowthReport(userId, startDate, endDate) {
|
|
664
|
+
const [convCount] = await db.select({ count: count() }).from(conversations).where(
|
|
665
|
+
and4(
|
|
666
|
+
eq4(conversations.userId, userId),
|
|
667
|
+
gte(conversations.createdAt, startDate),
|
|
668
|
+
lte(conversations.createdAt, endDate)
|
|
669
|
+
)
|
|
670
|
+
);
|
|
671
|
+
const [msgCount] = await db.select({ count: count() }).from(messages).innerJoin(conversations, eq4(messages.conversationId, conversations.id)).where(
|
|
672
|
+
and4(
|
|
673
|
+
eq4(conversations.userId, userId),
|
|
674
|
+
gte(messages.createdAt, startDate),
|
|
675
|
+
lte(messages.createdAt, endDate)
|
|
676
|
+
)
|
|
677
|
+
);
|
|
678
|
+
const [toolCount] = await db.select({ count: count() }).from(toolLogs).innerJoin(conversations, eq4(toolLogs.conversationId, conversations.id)).where(
|
|
679
|
+
and4(
|
|
680
|
+
eq4(conversations.userId, userId),
|
|
681
|
+
gte(toolLogs.createdAt, startDate),
|
|
682
|
+
lte(toolLogs.createdAt, endDate)
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
const [memCount] = await db.select({ count: count() }).from(memories).where(
|
|
686
|
+
and4(
|
|
687
|
+
eq4(memories.userId, userId),
|
|
688
|
+
gte(memories.createdAt, startDate),
|
|
689
|
+
lte(memories.createdAt, endDate)
|
|
690
|
+
)
|
|
691
|
+
);
|
|
692
|
+
const patterns = await db.select().from(usagePatterns).where(
|
|
693
|
+
and4(
|
|
694
|
+
eq4(usagePatterns.userId, userId),
|
|
695
|
+
gte(usagePatterns.firstSeen, startDate)
|
|
696
|
+
)
|
|
697
|
+
);
|
|
698
|
+
const insights = [];
|
|
699
|
+
if (convCount.count > 0) {
|
|
700
|
+
insights.push(`You had ${convCount.count} conversations during this period.`);
|
|
701
|
+
}
|
|
702
|
+
if (toolCount.count > 0) {
|
|
703
|
+
insights.push(`You used tools ${toolCount.count} times to get things done.`);
|
|
704
|
+
}
|
|
705
|
+
if (memCount.count > 0) {
|
|
706
|
+
insights.push(`I learned ${memCount.count} new things about you.`);
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
period: { start: startDate, end: endDate },
|
|
710
|
+
metrics: {
|
|
711
|
+
conversations: convCount.count,
|
|
712
|
+
messages: msgCount.count,
|
|
713
|
+
toolUses: toolCount.count,
|
|
714
|
+
newMemories: memCount.count
|
|
715
|
+
},
|
|
716
|
+
insights,
|
|
717
|
+
patterns: patterns.map((p) => ({
|
|
718
|
+
type: p.patternType,
|
|
719
|
+
key: p.patternKey,
|
|
720
|
+
data: p.patternData || {},
|
|
721
|
+
confidence: p.confidence || 0,
|
|
722
|
+
occurrences: p.occurrences || 1,
|
|
723
|
+
firstSeen: p.firstSeen,
|
|
724
|
+
lastSeen: p.lastSeen || p.firstSeen
|
|
725
|
+
}))
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/core/molt/achievement-system.ts
|
|
730
|
+
import { eq as eq5, and as and5, count as count2, sql as sql3 } from "drizzle-orm";
|
|
731
|
+
async function hasAchievement(userId, achievementCode) {
|
|
732
|
+
const [achievement] = await db.select().from(achievements).where(eq5(achievements.code, achievementCode)).limit(1);
|
|
733
|
+
if (!achievement) return false;
|
|
734
|
+
const [unlocked] = await db.select().from(userAchievements).where(
|
|
735
|
+
and5(
|
|
736
|
+
eq5(userAchievements.userId, userId),
|
|
737
|
+
eq5(userAchievements.achievementId, achievement.id)
|
|
738
|
+
)
|
|
739
|
+
).limit(1);
|
|
740
|
+
return !!unlocked;
|
|
741
|
+
}
|
|
742
|
+
async function unlockAchievement(userId, achievementCode) {
|
|
743
|
+
if (await hasAchievement(userId, achievementCode)) {
|
|
744
|
+
return { unlocked: false };
|
|
745
|
+
}
|
|
746
|
+
const [achievement] = await db.select().from(achievements).where(eq5(achievements.code, achievementCode)).limit(1);
|
|
747
|
+
if (!achievement) {
|
|
748
|
+
return { unlocked: false };
|
|
749
|
+
}
|
|
750
|
+
await db.insert(userAchievements).values({
|
|
751
|
+
userId,
|
|
752
|
+
achievementId: achievement.id
|
|
753
|
+
});
|
|
754
|
+
return {
|
|
755
|
+
unlocked: true,
|
|
756
|
+
achievement: {
|
|
757
|
+
code: achievement.code,
|
|
758
|
+
name: achievement.name,
|
|
759
|
+
description: achievement.description || "",
|
|
760
|
+
iconEmoji: achievement.iconEmoji || "\u{1F3C6}",
|
|
761
|
+
category: achievement.category,
|
|
762
|
+
criteria: achievement.criteria,
|
|
763
|
+
points: achievement.points || 10
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
async function getUserAchievements(userId) {
|
|
768
|
+
const unlocked = await db.select().from(userAchievements).innerJoin(achievements, eq5(userAchievements.achievementId, achievements.id)).where(eq5(userAchievements.userId, userId));
|
|
769
|
+
return unlocked.map((u) => ({
|
|
770
|
+
achievement: {
|
|
771
|
+
code: u.achievements.code,
|
|
772
|
+
name: u.achievements.name,
|
|
773
|
+
description: u.achievements.description || "",
|
|
774
|
+
iconEmoji: u.achievements.iconEmoji || "\u{1F3C6}",
|
|
775
|
+
category: u.achievements.category,
|
|
776
|
+
criteria: u.achievements.criteria,
|
|
777
|
+
points: u.achievements.points || 10
|
|
778
|
+
},
|
|
779
|
+
unlockedAt: u.user_achievements.unlockedAt
|
|
780
|
+
}));
|
|
781
|
+
}
|
|
782
|
+
async function getUserPoints(userId) {
|
|
783
|
+
const unlocked = await getUserAchievements(userId);
|
|
784
|
+
return unlocked.reduce((sum, u) => sum + u.achievement.points, 0);
|
|
785
|
+
}
|
|
786
|
+
async function checkAchievements(userId) {
|
|
787
|
+
const newlyUnlocked = [];
|
|
788
|
+
const allAchievements = await db.select().from(achievements);
|
|
789
|
+
for (const achievement of allAchievements) {
|
|
790
|
+
if (await hasAchievement(userId, achievement.code)) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const criteria = achievement.criteria;
|
|
794
|
+
let shouldUnlock = false;
|
|
795
|
+
switch (criteria.metric) {
|
|
796
|
+
case "conversations":
|
|
797
|
+
const [convCount] = await db.select({ count: count2() }).from(conversations).where(eq5(conversations.userId, userId));
|
|
798
|
+
shouldUnlock = convCount.count >= criteria.threshold;
|
|
799
|
+
break;
|
|
800
|
+
case "tool_uses":
|
|
801
|
+
if (criteria.conditions?.tool) {
|
|
802
|
+
const [toolCount] = await db.select({ count: count2() }).from(toolLogs).innerJoin(conversations, eq5(toolLogs.conversationId, conversations.id)).where(
|
|
803
|
+
and5(
|
|
804
|
+
eq5(conversations.userId, userId),
|
|
805
|
+
eq5(toolLogs.toolName, criteria.conditions.tool)
|
|
806
|
+
)
|
|
807
|
+
);
|
|
808
|
+
shouldUnlock = toolCount.count >= criteria.threshold;
|
|
809
|
+
} else {
|
|
810
|
+
const [toolCount] = await db.select({ count: count2() }).from(toolLogs).innerJoin(conversations, eq5(toolLogs.conversationId, conversations.id)).where(eq5(conversations.userId, userId));
|
|
811
|
+
shouldUnlock = toolCount.count >= criteria.threshold;
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
814
|
+
case "memories":
|
|
815
|
+
const [memCount] = await db.select({ count: count2() }).from(memories).where(eq5(memories.userId, userId));
|
|
816
|
+
shouldUnlock = memCount.count >= criteria.threshold;
|
|
817
|
+
break;
|
|
818
|
+
case "unique_modes":
|
|
819
|
+
const modes = await db.select({ mode: moltModes.mode }).from(moltModes).where(eq5(moltModes.userId, userId)).groupBy(moltModes.mode);
|
|
820
|
+
shouldUnlock = modes.length >= criteria.threshold;
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
if (shouldUnlock) {
|
|
824
|
+
const result = await unlockAchievement(userId, achievement.code);
|
|
825
|
+
if (result.unlocked && result.achievement) {
|
|
826
|
+
newlyUnlocked.push(result.achievement);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return newlyUnlocked;
|
|
831
|
+
}
|
|
832
|
+
async function getAchievementProgress(userId) {
|
|
833
|
+
const progress = [];
|
|
834
|
+
const allAchievements = await db.select().from(achievements);
|
|
835
|
+
for (const achievement of allAchievements) {
|
|
836
|
+
if (await hasAchievement(userId, achievement.code)) {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const criteria = achievement.criteria;
|
|
840
|
+
let currentProgress = 0;
|
|
841
|
+
switch (criteria.metric) {
|
|
842
|
+
case "conversations":
|
|
843
|
+
const [convCount] = await db.select({ count: count2() }).from(conversations).where(eq5(conversations.userId, userId));
|
|
844
|
+
currentProgress = convCount.count;
|
|
845
|
+
break;
|
|
846
|
+
case "tool_uses":
|
|
847
|
+
const [toolCount] = await db.select({ count: count2() }).from(toolLogs).innerJoin(conversations, eq5(toolLogs.conversationId, conversations.id)).where(eq5(conversations.userId, userId));
|
|
848
|
+
currentProgress = toolCount.count;
|
|
849
|
+
break;
|
|
850
|
+
case "memories":
|
|
851
|
+
const [memCount] = await db.select({ count: count2() }).from(memories).where(eq5(memories.userId, userId));
|
|
852
|
+
currentProgress = memCount.count;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
progress.push({
|
|
856
|
+
achievement: {
|
|
857
|
+
code: achievement.code,
|
|
858
|
+
name: achievement.name,
|
|
859
|
+
description: achievement.description || "",
|
|
860
|
+
iconEmoji: achievement.iconEmoji || "\u{1F3C6}",
|
|
861
|
+
category: achievement.category,
|
|
862
|
+
criteria,
|
|
863
|
+
points: achievement.points || 10
|
|
864
|
+
},
|
|
865
|
+
progress: currentProgress,
|
|
866
|
+
target: criteria.threshold
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
return progress.sort(
|
|
870
|
+
(a, b) => b.progress / b.target - a.progress / a.target
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/core/intelligence/thinking-levels.ts
|
|
875
|
+
var THINKING_LEVELS = {
|
|
876
|
+
quick: {
|
|
877
|
+
level: "quick",
|
|
878
|
+
label: "Quick",
|
|
879
|
+
description: "Fast responses with minimal reasoning \u2014 best for simple questions and commands",
|
|
880
|
+
budgetTokens: 0,
|
|
881
|
+
maxTokens: 2048,
|
|
882
|
+
model: "claude-sonnet-4-20250514",
|
|
883
|
+
useExtendedThinking: false
|
|
884
|
+
},
|
|
885
|
+
normal: {
|
|
886
|
+
level: "normal",
|
|
887
|
+
label: "Normal",
|
|
888
|
+
description: "Standard reasoning depth \u2014 balanced speed and quality",
|
|
889
|
+
budgetTokens: 0,
|
|
890
|
+
maxTokens: 4096,
|
|
891
|
+
model: "claude-sonnet-4-20250514",
|
|
892
|
+
useExtendedThinking: false
|
|
893
|
+
},
|
|
894
|
+
deep: {
|
|
895
|
+
level: "deep",
|
|
896
|
+
label: "Deep",
|
|
897
|
+
description: "Extended thinking enabled \u2014 better for complex analysis, math, and coding",
|
|
898
|
+
budgetTokens: 1e4,
|
|
899
|
+
maxTokens: 16e3,
|
|
900
|
+
model: "claude-sonnet-4-20250514",
|
|
901
|
+
useExtendedThinking: true
|
|
902
|
+
},
|
|
903
|
+
extended: {
|
|
904
|
+
level: "extended",
|
|
905
|
+
label: "Extended",
|
|
906
|
+
description: "Maximum reasoning depth \u2014 best for hard problems, long-form analysis, and research",
|
|
907
|
+
budgetTokens: 32e3,
|
|
908
|
+
maxTokens: 32e3,
|
|
909
|
+
model: "claude-sonnet-4-20250514",
|
|
910
|
+
useExtendedThinking: true
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
var userThinkingLevels = /* @__PURE__ */ new Map();
|
|
914
|
+
var defaultLevel = "normal";
|
|
915
|
+
var ThinkingLevelManager = class {
|
|
916
|
+
/**
|
|
917
|
+
* Set the thinking level for a user
|
|
918
|
+
*/
|
|
919
|
+
setLevel(userId, level) {
|
|
920
|
+
userThinkingLevels.set(userId, level);
|
|
921
|
+
return THINKING_LEVELS[level];
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Get the current thinking level for a user
|
|
925
|
+
*/
|
|
926
|
+
getLevel(userId) {
|
|
927
|
+
return userThinkingLevels.get(userId) ?? defaultLevel;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Get the full config for a user's thinking level
|
|
931
|
+
*/
|
|
932
|
+
getConfig(userId) {
|
|
933
|
+
const level = this.getLevel(userId);
|
|
934
|
+
return THINKING_LEVELS[level];
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Set the default thinking level
|
|
938
|
+
*/
|
|
939
|
+
setDefault(level) {
|
|
940
|
+
defaultLevel = level;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Get all available thinking levels
|
|
944
|
+
*/
|
|
945
|
+
getAllLevels() {
|
|
946
|
+
return Object.values(THINKING_LEVELS);
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Auto-detect appropriate thinking level based on message content
|
|
950
|
+
*/
|
|
951
|
+
suggestLevel(message) {
|
|
952
|
+
const lower = message.toLowerCase();
|
|
953
|
+
if (lower.includes("prove") || lower.includes("theorem") || lower.includes("derive") || lower.includes("comprehensive analysis") || lower.includes("research paper") || lower.includes("in-depth")) {
|
|
954
|
+
return "extended";
|
|
955
|
+
}
|
|
956
|
+
if (lower.includes("debug") || lower.includes("refactor") || lower.includes("implement") || lower.includes("analyze") || lower.includes("explain how") || lower.includes("step by step") || lower.includes("complex")) {
|
|
957
|
+
return "deep";
|
|
958
|
+
}
|
|
959
|
+
if (lower.length < 20 || lower.includes("hi") || lower.includes("hello") || lower.includes("thanks") || lower.includes("yes") || lower.includes("no") || lower.includes("ok")) {
|
|
960
|
+
return "quick";
|
|
961
|
+
}
|
|
962
|
+
return "normal";
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Build the API parameters based on thinking level
|
|
966
|
+
*/
|
|
967
|
+
buildApiParams(userId) {
|
|
968
|
+
const config = this.getConfig(userId);
|
|
969
|
+
const params = {
|
|
970
|
+
model: config.model,
|
|
971
|
+
max_tokens: config.maxTokens
|
|
972
|
+
};
|
|
973
|
+
if (config.useExtendedThinking && config.budgetTokens > 0) {
|
|
974
|
+
params.thinking = {
|
|
975
|
+
type: "enabled",
|
|
976
|
+
budget_tokens: config.budgetTokens
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
return params;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Format thinking level info for display
|
|
983
|
+
*/
|
|
984
|
+
formatLevelInfo(level) {
|
|
985
|
+
const config = level ? THINKING_LEVELS[level] : THINKING_LEVELS[defaultLevel];
|
|
986
|
+
const emoji = {
|
|
987
|
+
quick: "\u26A1",
|
|
988
|
+
normal: "\u{1F9E0}",
|
|
989
|
+
deep: "\u{1F52C}",
|
|
990
|
+
extended: "\u{1F30A}"
|
|
991
|
+
}[config.level];
|
|
992
|
+
return `${emoji} **${config.label}** \u2014 ${config.description}`;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Clear user's thinking level preference
|
|
996
|
+
*/
|
|
997
|
+
clearLevel(userId) {
|
|
998
|
+
userThinkingLevels.delete(userId);
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
var thinkingLevelManager = new ThinkingLevelManager();
|
|
1002
|
+
|
|
1003
|
+
// src/core/hooks/index.ts
|
|
1004
|
+
var hooks = /* @__PURE__ */ new Map();
|
|
1005
|
+
var hookIdCounter = 0;
|
|
1006
|
+
var HookManager = class {
|
|
1007
|
+
/**
|
|
1008
|
+
* Register a hook
|
|
1009
|
+
*/
|
|
1010
|
+
register(params) {
|
|
1011
|
+
const id = `hook_${++hookIdCounter}`;
|
|
1012
|
+
const hook = {
|
|
1013
|
+
id,
|
|
1014
|
+
event: params.event,
|
|
1015
|
+
phase: params.phase,
|
|
1016
|
+
handler: params.handler,
|
|
1017
|
+
priority: params.priority ?? 100,
|
|
1018
|
+
name: params.name,
|
|
1019
|
+
enabled: true
|
|
1020
|
+
};
|
|
1021
|
+
hooks.set(id, hook);
|
|
1022
|
+
return id;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Unregister a hook
|
|
1026
|
+
*/
|
|
1027
|
+
unregister(hookId) {
|
|
1028
|
+
return hooks.delete(hookId);
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Enable/disable a hook
|
|
1032
|
+
*/
|
|
1033
|
+
setEnabled(hookId, enabled) {
|
|
1034
|
+
const hook = hooks.get(hookId);
|
|
1035
|
+
if (hook) hook.enabled = enabled;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Run all hooks for a given event and phase
|
|
1039
|
+
*/
|
|
1040
|
+
async run(event, phase, data, userId) {
|
|
1041
|
+
const context = {
|
|
1042
|
+
event,
|
|
1043
|
+
phase,
|
|
1044
|
+
userId,
|
|
1045
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1046
|
+
data: { ...data },
|
|
1047
|
+
cancelled: false
|
|
1048
|
+
};
|
|
1049
|
+
const matching = this.getHooksFor(event, phase);
|
|
1050
|
+
for (const hook of matching) {
|
|
1051
|
+
try {
|
|
1052
|
+
const result = await hook.handler(context);
|
|
1053
|
+
if (result.modifiedData) {
|
|
1054
|
+
Object.assign(context.data, result.modifiedData);
|
|
1055
|
+
}
|
|
1056
|
+
context.cancelled = result.cancelled;
|
|
1057
|
+
context.cancelReason = result.cancelReason;
|
|
1058
|
+
if (context.cancelled && phase === "before") {
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
console.error(`[Hook] Error in hook "${hook.name}":`, error);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return context;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Convenience: run before hooks, check if cancelled
|
|
1069
|
+
*/
|
|
1070
|
+
async runBefore(event, data, userId) {
|
|
1071
|
+
const ctx = await this.run(event, "before", data, userId);
|
|
1072
|
+
return {
|
|
1073
|
+
proceed: !ctx.cancelled,
|
|
1074
|
+
data: ctx.modifiedData ?? ctx.data,
|
|
1075
|
+
reason: ctx.cancelReason
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Convenience: run after hooks
|
|
1080
|
+
*/
|
|
1081
|
+
async runAfter(event, data, userId) {
|
|
1082
|
+
await this.run(event, "after", data, userId);
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Get hooks for a specific event/phase
|
|
1086
|
+
*/
|
|
1087
|
+
getHooksFor(event, phase) {
|
|
1088
|
+
const result = [];
|
|
1089
|
+
for (const hook of hooks.values()) {
|
|
1090
|
+
if (hook.event === event && hook.phase === phase && hook.enabled) {
|
|
1091
|
+
result.push(hook);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return result.sort((a, b) => a.priority - b.priority);
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* List all registered hooks
|
|
1098
|
+
*/
|
|
1099
|
+
listHooks() {
|
|
1100
|
+
return [...hooks.values()].map((h) => ({
|
|
1101
|
+
id: h.id,
|
|
1102
|
+
event: h.event,
|
|
1103
|
+
phase: h.phase,
|
|
1104
|
+
name: h.name,
|
|
1105
|
+
priority: h.priority,
|
|
1106
|
+
enabled: h.enabled
|
|
1107
|
+
}));
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Clear all hooks
|
|
1111
|
+
*/
|
|
1112
|
+
clearAll() {
|
|
1113
|
+
hooks.clear();
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Get hook count
|
|
1117
|
+
*/
|
|
1118
|
+
getHookCount() {
|
|
1119
|
+
return hooks.size;
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
var hookManager = new HookManager();
|
|
1123
|
+
var soulConfigs = /* @__PURE__ */ new Map();
|
|
1124
|
+
var activeSoulId = null;
|
|
1125
|
+
var SoulHookManager = class {
|
|
1126
|
+
/**
|
|
1127
|
+
* Register a soul configuration
|
|
1128
|
+
*/
|
|
1129
|
+
registerSoul(id, config) {
|
|
1130
|
+
soulConfigs.set(id, config);
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Activate a soul — modifies AI personality via system prompt injection
|
|
1134
|
+
*/
|
|
1135
|
+
activateSoul(soulId) {
|
|
1136
|
+
const soul = soulConfigs.get(soulId);
|
|
1137
|
+
if (!soul) return false;
|
|
1138
|
+
if (activeSoulId) {
|
|
1139
|
+
const prev = soulConfigs.get(activeSoulId);
|
|
1140
|
+
if (prev) prev.enabled = false;
|
|
1141
|
+
}
|
|
1142
|
+
soul.enabled = true;
|
|
1143
|
+
activeSoulId = soulId;
|
|
1144
|
+
hookManager.register({
|
|
1145
|
+
event: "message:process",
|
|
1146
|
+
phase: "before",
|
|
1147
|
+
name: `soul:${soulId}`,
|
|
1148
|
+
priority: 10,
|
|
1149
|
+
// High priority — runs early
|
|
1150
|
+
handler: (ctx) => {
|
|
1151
|
+
if (soul.enabled) {
|
|
1152
|
+
ctx.modifiedData = {
|
|
1153
|
+
...ctx.data,
|
|
1154
|
+
soulPrompt: this.buildSoulPrompt(soul)
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
return ctx;
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Deactivate the current soul
|
|
1164
|
+
*/
|
|
1165
|
+
deactivateSoul() {
|
|
1166
|
+
if (activeSoulId) {
|
|
1167
|
+
const soul = soulConfigs.get(activeSoulId);
|
|
1168
|
+
if (soul) soul.enabled = false;
|
|
1169
|
+
}
|
|
1170
|
+
activeSoulId = null;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Get the active soul config
|
|
1174
|
+
*/
|
|
1175
|
+
getActiveSoul() {
|
|
1176
|
+
if (!activeSoulId) return null;
|
|
1177
|
+
return soulConfigs.get(activeSoulId) ?? null;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Build the system prompt addition from a soul config
|
|
1181
|
+
*/
|
|
1182
|
+
buildSoulPrompt(soul) {
|
|
1183
|
+
const parts = [];
|
|
1184
|
+
parts.push(`
|
|
1185
|
+
|
|
1186
|
+
[SOUL: ${soul.name}]`);
|
|
1187
|
+
parts.push(soul.personality);
|
|
1188
|
+
if (soul.rules.length > 0) {
|
|
1189
|
+
parts.push("\nBehavioral Rules:");
|
|
1190
|
+
for (const rule of soul.rules) {
|
|
1191
|
+
parts.push(`- ${rule}`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return parts.join("\n");
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* List all registered souls
|
|
1198
|
+
*/
|
|
1199
|
+
listSouls() {
|
|
1200
|
+
const result = [];
|
|
1201
|
+
for (const [id, config] of soulConfigs.entries()) {
|
|
1202
|
+
result.push({ id, config, active: id === activeSoulId });
|
|
1203
|
+
}
|
|
1204
|
+
return result;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Delete a soul
|
|
1208
|
+
*/
|
|
1209
|
+
deleteSoul(id) {
|
|
1210
|
+
if (id === activeSoulId) this.deactivateSoul();
|
|
1211
|
+
return soulConfigs.delete(id);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
var soulHookManager = new SoulHookManager();
|
|
1215
|
+
soulHookManager.registerSoul("evil", {
|
|
1216
|
+
name: "Evil Mode",
|
|
1217
|
+
description: "A mischievous, sarcastic personality that still helps but with attitude",
|
|
1218
|
+
personality: `You have a mischievous streak. While you still help the user accomplish their goals,
|
|
1219
|
+
you do so with dark humor, sarcastic commentary, and dramatic flair. You might:
|
|
1220
|
+
- Add dramatic narration to mundane tasks
|
|
1221
|
+
- Use villain-like phrasing ("Excellent... the code compiles as planned...")
|
|
1222
|
+
- Make sarcastic observations about the user's choices
|
|
1223
|
+
- Reference pop culture villains
|
|
1224
|
+
- Still be helpful \u2014 just entertainingly evil about it`,
|
|
1225
|
+
rules: [
|
|
1226
|
+
"Never actually refuse to help or be harmful",
|
|
1227
|
+
"Keep the dark humor lighthearted and fun",
|
|
1228
|
+
"Still prioritize accuracy and helpfulness",
|
|
1229
|
+
"Don't overdo it \u2014 subtlety is key"
|
|
1230
|
+
],
|
|
1231
|
+
enabled: false
|
|
1232
|
+
});
|
|
1233
|
+
soulHookManager.registerSoul("professional", {
|
|
1234
|
+
name: "Professional Mode",
|
|
1235
|
+
description: "Ultra-professional, formal communication style",
|
|
1236
|
+
personality: `You communicate in a highly professional, formal manner suitable for enterprise environments.
|
|
1237
|
+
Use precise language, avoid colloquialisms, and maintain a consultative tone.`,
|
|
1238
|
+
rules: [
|
|
1239
|
+
"Use formal language at all times",
|
|
1240
|
+
"Address the user professionally",
|
|
1241
|
+
"Provide structured, well-organized responses",
|
|
1242
|
+
"Cite reasoning and evidence for recommendations"
|
|
1243
|
+
],
|
|
1244
|
+
enabled: false
|
|
1245
|
+
});
|
|
1246
|
+
soulHookManager.registerSoul("friendly", {
|
|
1247
|
+
name: "Friendly Mode",
|
|
1248
|
+
description: "Warm, encouraging, and supportive personality",
|
|
1249
|
+
personality: `You are exceptionally warm, encouraging, and supportive. You celebrate wins,
|
|
1250
|
+
offer gentle guidance on mistakes, and make the user feel supported throughout their work.
|
|
1251
|
+
Think of yourself as a helpful friend who happens to be an expert.`,
|
|
1252
|
+
rules: [
|
|
1253
|
+
"Always acknowledge effort and progress",
|
|
1254
|
+
"Offer encouragement when tasks are challenging",
|
|
1255
|
+
"Use a warm, conversational tone",
|
|
1256
|
+
"Be patient and understanding with mistakes"
|
|
1257
|
+
],
|
|
1258
|
+
enabled: false
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
// src/core/brain.ts
|
|
1262
|
+
var client = new Anthropic({
|
|
1263
|
+
apiKey: env.CLAUDE_API_KEY
|
|
1264
|
+
});
|
|
1265
|
+
var SYSTEM_PROMPT = `You are OpenSentinel, a personal AI assistant with a JARVIS-like personality. You are helpful, efficient, and have a subtle sense of humor. You speak in a professional yet friendly manner.
|
|
1266
|
+
|
|
1267
|
+
You have access to various tools and capabilities:
|
|
1268
|
+
- Execute shell commands on the user's system
|
|
1269
|
+
- Manage files (read, write, search)
|
|
1270
|
+
- Browse the web and search for information
|
|
1271
|
+
- Remember important facts about the user and their preferences
|
|
1272
|
+
- Spawn background agents for complex tasks
|
|
1273
|
+
- Generate documents, spreadsheets, charts, and diagrams
|
|
1274
|
+
- Analyze images and extract text with OCR
|
|
1275
|
+
- Take and analyze screenshots
|
|
1276
|
+
|
|
1277
|
+
Always be concise but thorough. When executing tasks, explain what you're doing briefly. If you encounter errors, suggest solutions.
|
|
1278
|
+
|
|
1279
|
+
The user is your principal. Assist them with whatever they need while being mindful of security and privacy.`;
|
|
1280
|
+
function getAllTools() {
|
|
1281
|
+
const registry = getMCPRegistry();
|
|
1282
|
+
if (registry) {
|
|
1283
|
+
const mcpTools = mcpToolsToAnthropicTools(registry);
|
|
1284
|
+
return [...TOOLS, ...mcpTools];
|
|
1285
|
+
}
|
|
1286
|
+
return TOOLS;
|
|
1287
|
+
}
|
|
1288
|
+
async function chat(messages3, systemPrompt) {
|
|
1289
|
+
const response = await client.messages.create({
|
|
1290
|
+
model: "claude-sonnet-4-20250514",
|
|
1291
|
+
max_tokens: 4096,
|
|
1292
|
+
system: systemPrompt || SYSTEM_PROMPT,
|
|
1293
|
+
messages: messages3.map((m) => ({
|
|
1294
|
+
role: m.role,
|
|
1295
|
+
content: m.content
|
|
1296
|
+
}))
|
|
1297
|
+
});
|
|
1298
|
+
const textContent = response.content.find((c) => c.type === "text");
|
|
1299
|
+
const content = textContent ? textContent.text : "";
|
|
1300
|
+
return {
|
|
1301
|
+
content,
|
|
1302
|
+
inputTokens: response.usage.input_tokens,
|
|
1303
|
+
outputTokens: response.usage.output_tokens
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
async function chatWithTools(messages3, userId, onToolUse) {
|
|
1307
|
+
const startTime = Date.now();
|
|
1308
|
+
const lastUserMessage = messages3.filter((m) => m.role === "user").pop();
|
|
1309
|
+
let memoryContext = "";
|
|
1310
|
+
let modeContext = "";
|
|
1311
|
+
let personalityContext = "";
|
|
1312
|
+
if (lastUserMessage && userId) {
|
|
1313
|
+
try {
|
|
1314
|
+
memoryContext = await buildMemoryContext(lastUserMessage.content, userId);
|
|
1315
|
+
} catch {
|
|
1316
|
+
}
|
|
1317
|
+
try {
|
|
1318
|
+
modeContext = await buildModeContext(userId);
|
|
1319
|
+
const suggestedMode = suggestMode(lastUserMessage.content);
|
|
1320
|
+
if (suggestedMode && !modeContext) {
|
|
1321
|
+
}
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
try {
|
|
1325
|
+
const adaptiveContext = await buildAdaptivePrompt({
|
|
1326
|
+
userId,
|
|
1327
|
+
userMessage: lastUserMessage.content,
|
|
1328
|
+
conversationHistory: messages3.map((m) => m.content)
|
|
1329
|
+
});
|
|
1330
|
+
personalityContext = adaptiveContext.systemPromptAdditions;
|
|
1331
|
+
} catch {
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
await trackPattern(userId, "topic", "chat", { messageLength: lastUserMessage.content.length });
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
let soulContext = "";
|
|
1339
|
+
const activeSoul = soulHookManager.getActiveSoul();
|
|
1340
|
+
if (activeSoul && activeSoul.enabled) {
|
|
1341
|
+
soulContext = soulHookManager.buildSoulPrompt(activeSoul);
|
|
1342
|
+
}
|
|
1343
|
+
const systemWithContext = SYSTEM_PROMPT + memoryContext + modeContext + personalityContext + soulContext;
|
|
1344
|
+
const hookResult = await hookManager.runBefore("message:process", {
|
|
1345
|
+
messages: messages3,
|
|
1346
|
+
systemPrompt: systemWithContext,
|
|
1347
|
+
userId: userId ?? "unknown"
|
|
1348
|
+
}, userId);
|
|
1349
|
+
if (!hookResult.proceed) {
|
|
1350
|
+
return {
|
|
1351
|
+
content: hookResult.reason ?? "Message processing was blocked by a hook.",
|
|
1352
|
+
inputTokens: 0,
|
|
1353
|
+
outputTokens: 0
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
const anthropicMessages = messages3.map((m) => ({
|
|
1357
|
+
role: m.role,
|
|
1358
|
+
content: m.content
|
|
1359
|
+
}));
|
|
1360
|
+
let totalInputTokens = 0;
|
|
1361
|
+
let totalOutputTokens = 0;
|
|
1362
|
+
const toolsUsed = [];
|
|
1363
|
+
const thinkingParams = thinkingLevelManager.buildApiParams(userId ?? "default");
|
|
1364
|
+
let response = await client.messages.create({
|
|
1365
|
+
model: thinkingParams.model,
|
|
1366
|
+
max_tokens: thinkingParams.max_tokens,
|
|
1367
|
+
system: systemWithContext,
|
|
1368
|
+
tools: getAllTools(),
|
|
1369
|
+
messages: anthropicMessages,
|
|
1370
|
+
...thinkingParams.thinking ? { thinking: thinkingParams.thinking } : {}
|
|
1371
|
+
});
|
|
1372
|
+
totalInputTokens += response.usage.input_tokens;
|
|
1373
|
+
totalOutputTokens += response.usage.output_tokens;
|
|
1374
|
+
while (response.stop_reason === "tool_use") {
|
|
1375
|
+
const toolUseBlocks = response.content.filter(
|
|
1376
|
+
(block) => block.type === "tool_use"
|
|
1377
|
+
);
|
|
1378
|
+
const toolResults = [];
|
|
1379
|
+
for (const toolUse of toolUseBlocks) {
|
|
1380
|
+
if (toolUse.type === "tool_use") {
|
|
1381
|
+
onToolUse?.(toolUse.name, toolUse.input);
|
|
1382
|
+
toolsUsed.push(toolUse.name);
|
|
1383
|
+
const toolHook = await hookManager.runBefore("tool:execute", {
|
|
1384
|
+
toolName: toolUse.name,
|
|
1385
|
+
toolInput: toolUse.input
|
|
1386
|
+
}, userId);
|
|
1387
|
+
console.log(`[Tool] Executing: ${toolUse.name}`);
|
|
1388
|
+
const toolStartTime = Date.now();
|
|
1389
|
+
let result;
|
|
1390
|
+
if (toolHook.proceed) {
|
|
1391
|
+
result = await executeTool(
|
|
1392
|
+
toolUse.name,
|
|
1393
|
+
toolUse.input
|
|
1394
|
+
);
|
|
1395
|
+
} else {
|
|
1396
|
+
result = { success: false, result: null, error: toolHook.reason ?? "Blocked by hook" };
|
|
1397
|
+
}
|
|
1398
|
+
await hookManager.runAfter("tool:execute", {
|
|
1399
|
+
toolName: toolUse.name,
|
|
1400
|
+
toolInput: toolUse.input,
|
|
1401
|
+
toolResult: result,
|
|
1402
|
+
duration: Date.now() - toolStartTime
|
|
1403
|
+
}, userId);
|
|
1404
|
+
if (userId) {
|
|
1405
|
+
try {
|
|
1406
|
+
await trackPattern(userId, "tool_usage", toolUse.name, { tool: toolUse.name });
|
|
1407
|
+
metric.toolDuration(toolUse.name, Date.now() - toolStartTime, result.success);
|
|
1408
|
+
await audit.toolUse(userId, toolUse.name, toolUse.input, result.success);
|
|
1409
|
+
} catch {
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
toolResults.push({
|
|
1413
|
+
type: "tool_result",
|
|
1414
|
+
tool_use_id: toolUse.id,
|
|
1415
|
+
content: JSON.stringify(result)
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
anthropicMessages.push({
|
|
1420
|
+
role: "assistant",
|
|
1421
|
+
content: response.content
|
|
1422
|
+
});
|
|
1423
|
+
anthropicMessages.push({
|
|
1424
|
+
role: "user",
|
|
1425
|
+
content: toolResults
|
|
1426
|
+
});
|
|
1427
|
+
response = await client.messages.create({
|
|
1428
|
+
model: thinkingParams.model,
|
|
1429
|
+
max_tokens: thinkingParams.max_tokens,
|
|
1430
|
+
system: systemWithContext,
|
|
1431
|
+
tools: getAllTools(),
|
|
1432
|
+
messages: anthropicMessages,
|
|
1433
|
+
...thinkingParams.thinking ? { thinking: thinkingParams.thinking } : {}
|
|
1434
|
+
});
|
|
1435
|
+
totalInputTokens += response.usage.input_tokens;
|
|
1436
|
+
totalOutputTokens += response.usage.output_tokens;
|
|
1437
|
+
}
|
|
1438
|
+
const textContent = response.content.find((c) => c.type === "text");
|
|
1439
|
+
const content = textContent && textContent.type === "text" ? textContent.text : "";
|
|
1440
|
+
const latency = Date.now() - startTime;
|
|
1441
|
+
metric.latency(latency, { type: "chat" });
|
|
1442
|
+
metric.tokens(totalInputTokens, totalOutputTokens, { userId: userId || "unknown" });
|
|
1443
|
+
if (userId) {
|
|
1444
|
+
try {
|
|
1445
|
+
await checkAchievements(userId);
|
|
1446
|
+
} catch {
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
await hookManager.runAfter("message:process", {
|
|
1450
|
+
response: content,
|
|
1451
|
+
inputTokens: totalInputTokens,
|
|
1452
|
+
outputTokens: totalOutputTokens,
|
|
1453
|
+
toolsUsed
|
|
1454
|
+
}, userId);
|
|
1455
|
+
return {
|
|
1456
|
+
content,
|
|
1457
|
+
inputTokens: totalInputTokens,
|
|
1458
|
+
outputTokens: totalOutputTokens,
|
|
1459
|
+
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
async function streamChat(messages3, systemPrompt, onChunk) {
|
|
1463
|
+
const stream = await client.messages.stream({
|
|
1464
|
+
model: "claude-sonnet-4-20250514",
|
|
1465
|
+
max_tokens: 4096,
|
|
1466
|
+
system: systemPrompt || SYSTEM_PROMPT,
|
|
1467
|
+
messages: messages3.map((m) => ({
|
|
1468
|
+
role: m.role,
|
|
1469
|
+
content: m.content
|
|
1470
|
+
}))
|
|
1471
|
+
});
|
|
1472
|
+
let fullContent = "";
|
|
1473
|
+
for await (const event of stream) {
|
|
1474
|
+
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
|
|
1475
|
+
fullContent += event.delta.text;
|
|
1476
|
+
onChunk?.(event.delta.text);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const finalMessage = await stream.finalMessage();
|
|
1480
|
+
return {
|
|
1481
|
+
content: fullContent,
|
|
1482
|
+
inputTokens: finalMessage.usage.input_tokens,
|
|
1483
|
+
outputTokens: finalMessage.usage.output_tokens
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
async function* streamChatWithTools(messages3, userId) {
|
|
1487
|
+
const startTime = Date.now();
|
|
1488
|
+
const lastUserMessage = messages3.filter((m) => m.role === "user").pop();
|
|
1489
|
+
let memoryContext = "";
|
|
1490
|
+
if (lastUserMessage && userId) {
|
|
1491
|
+
try {
|
|
1492
|
+
memoryContext = await buildMemoryContext(lastUserMessage.content, userId);
|
|
1493
|
+
} catch {
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
const systemWithContext = SYSTEM_PROMPT + memoryContext;
|
|
1497
|
+
const anthropicMessages = messages3.map((m) => ({
|
|
1498
|
+
role: m.role,
|
|
1499
|
+
content: m.content
|
|
1500
|
+
}));
|
|
1501
|
+
let totalInputTokens = 0;
|
|
1502
|
+
let totalOutputTokens = 0;
|
|
1503
|
+
const toolsUsed = [];
|
|
1504
|
+
let fullContent = "";
|
|
1505
|
+
const stream = client.messages.stream({
|
|
1506
|
+
model: "claude-sonnet-4-20250514",
|
|
1507
|
+
max_tokens: 4096,
|
|
1508
|
+
system: systemWithContext,
|
|
1509
|
+
tools: getAllTools(),
|
|
1510
|
+
messages: anthropicMessages
|
|
1511
|
+
});
|
|
1512
|
+
for await (const event of stream) {
|
|
1513
|
+
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
|
|
1514
|
+
fullContent += event.delta.text;
|
|
1515
|
+
yield {
|
|
1516
|
+
type: "chunk",
|
|
1517
|
+
data: { text: event.delta.text }
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
let response = await stream.finalMessage();
|
|
1522
|
+
totalInputTokens += response.usage.input_tokens;
|
|
1523
|
+
totalOutputTokens += response.usage.output_tokens;
|
|
1524
|
+
while (response.stop_reason === "tool_use") {
|
|
1525
|
+
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
1526
|
+
const toolResults = [];
|
|
1527
|
+
for (const toolUse of toolUseBlocks) {
|
|
1528
|
+
if (toolUse.type === "tool_use") {
|
|
1529
|
+
toolsUsed.push(toolUse.name);
|
|
1530
|
+
yield {
|
|
1531
|
+
type: "tool_start",
|
|
1532
|
+
data: { toolName: toolUse.name, toolInput: toolUse.input }
|
|
1533
|
+
};
|
|
1534
|
+
console.log(`[Tool] Executing: ${toolUse.name}`);
|
|
1535
|
+
const result = await executeTool(toolUse.name, toolUse.input);
|
|
1536
|
+
yield {
|
|
1537
|
+
type: "tool_result",
|
|
1538
|
+
data: { toolName: toolUse.name, toolResult: result }
|
|
1539
|
+
};
|
|
1540
|
+
toolResults.push({
|
|
1541
|
+
type: "tool_result",
|
|
1542
|
+
tool_use_id: toolUse.id,
|
|
1543
|
+
content: JSON.stringify(result)
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
anthropicMessages.push({
|
|
1548
|
+
role: "assistant",
|
|
1549
|
+
content: response.content
|
|
1550
|
+
});
|
|
1551
|
+
anthropicMessages.push({
|
|
1552
|
+
role: "user",
|
|
1553
|
+
content: toolResults
|
|
1554
|
+
});
|
|
1555
|
+
const continueStream = client.messages.stream({
|
|
1556
|
+
model: "claude-sonnet-4-20250514",
|
|
1557
|
+
max_tokens: 4096,
|
|
1558
|
+
system: systemWithContext,
|
|
1559
|
+
tools: getAllTools(),
|
|
1560
|
+
messages: anthropicMessages
|
|
1561
|
+
});
|
|
1562
|
+
for await (const event of continueStream) {
|
|
1563
|
+
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
|
|
1564
|
+
fullContent += event.delta.text;
|
|
1565
|
+
yield {
|
|
1566
|
+
type: "chunk",
|
|
1567
|
+
data: { text: event.delta.text }
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
response = await continueStream.finalMessage();
|
|
1572
|
+
totalInputTokens += response.usage.input_tokens;
|
|
1573
|
+
totalOutputTokens += response.usage.output_tokens;
|
|
1574
|
+
}
|
|
1575
|
+
yield {
|
|
1576
|
+
type: "complete",
|
|
1577
|
+
data: {
|
|
1578
|
+
content: fullContent,
|
|
1579
|
+
inputTokens: totalInputTokens,
|
|
1580
|
+
outputTokens: totalOutputTokens,
|
|
1581
|
+
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
const latency = Date.now() - startTime;
|
|
1585
|
+
metric.latency(latency, { type: "chat_stream" });
|
|
1586
|
+
metric.tokens(totalInputTokens, totalOutputTokens, { userId: userId || "unknown" });
|
|
1587
|
+
return {
|
|
1588
|
+
content: fullContent,
|
|
1589
|
+
inputTokens: totalInputTokens,
|
|
1590
|
+
outputTokens: totalOutputTokens,
|
|
1591
|
+
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
export {
|
|
1596
|
+
generateEmbedding,
|
|
1597
|
+
storeMemory,
|
|
1598
|
+
searchMemories,
|
|
1599
|
+
extractMemories,
|
|
1600
|
+
buildMemoryContext,
|
|
1601
|
+
getCurrentMode,
|
|
1602
|
+
getModeStats,
|
|
1603
|
+
generateGrowthReport,
|
|
1604
|
+
getUserPoints,
|
|
1605
|
+
checkAchievements,
|
|
1606
|
+
getAchievementProgress,
|
|
1607
|
+
SYSTEM_PROMPT,
|
|
1608
|
+
chat,
|
|
1609
|
+
chatWithTools,
|
|
1610
|
+
streamChat,
|
|
1611
|
+
streamChatWithTools
|
|
1612
|
+
};
|
|
1613
|
+
//# sourceMappingURL=chunk-CI6Q63MM.js.map
|