obol-ai 0.3.7 → 0.3.9
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/CHANGELOG.md +3 -0
- package/package.json +1 -1
- package/src/analysis.js +11 -7
- package/src/claude/router.js +16 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "obol-ai",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Self-evolving AI assistant that learns, remembers, and acts on its own. Persistent vector memory, self-rewriting personality, proactive heartbeats.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/analysis.js
CHANGED
|
@@ -124,10 +124,10 @@ async function structureReport(client, report, scheduler, patterns, chatId, time
|
|
|
124
124
|
items: {
|
|
125
125
|
type: 'object',
|
|
126
126
|
properties: {
|
|
127
|
-
key: { type: 'string', description: 'Stable identifier e.g. "timing.active_hours"' },
|
|
127
|
+
key: { type: 'string', description: 'Stable dot-notation identifier for this pattern, e.g. "timing.active_hours", "mood.stress_signals", "humor.style"' },
|
|
128
128
|
dimension: { type: 'string', enum: ['timing', 'mood', 'humor', 'engagement', 'communication', 'topics'] },
|
|
129
|
-
summary: { type: 'string', description: '
|
|
130
|
-
data: { type: 'object', description: '
|
|
129
|
+
summary: { type: 'string', description: 'Factual observation about the user, e.g. "Most active between 7-10pm on weekdays", "Uses sarcasm and dry humor when relaxed"' },
|
|
130
|
+
data: { type: 'object', description: 'Factual evidence only. Examples: {"peak_hours":["19:00-22:00"],"peak_days":["mon","wed","fri"]} or {"preferred_topics":["crypto","music"]} or {"avg_message_length":"short","uses_caps":false}. Never put notes, commentary, or meta-analysis here.' },
|
|
131
131
|
confidence: { type: 'number', description: '0-1' },
|
|
132
132
|
},
|
|
133
133
|
required: ['key', 'dimension', 'summary', 'confidence'],
|
|
@@ -139,9 +139,11 @@ async function structureReport(client, report, scheduler, patterns, chatId, time
|
|
|
139
139
|
}];
|
|
140
140
|
|
|
141
141
|
try {
|
|
142
|
+
const patternGuidance = `Extract behavioral patterns about this user from the report. Each pattern must be a factual observation about the user's behavior — not notes about your analysis process. If you see the same pattern in the existing list, reuse its exact key and update the summary/confidence. Skip patterns already at confidence >0.8 unless new evidence contradicts them.`;
|
|
143
|
+
|
|
142
144
|
const system = formattedPatterns
|
|
143
|
-
? `Existing behavioral patterns for this user:\n${formattedPatterns}\n\n---\n\
|
|
144
|
-
:
|
|
145
|
+
? `Existing behavioral patterns for this user:\n${formattedPatterns}\n\n---\n\n${patternGuidance}`
|
|
146
|
+
: `Convert this analytical report into structured data using the save_analysis tool. ${patternGuidance}`;
|
|
145
147
|
|
|
146
148
|
const response = await client.messages.create({
|
|
147
149
|
model: 'claude-sonnet-4-6',
|
|
@@ -169,8 +171,10 @@ async function structureReport(client, report, scheduler, patterns, chatId, time
|
|
|
169
171
|
|
|
170
172
|
for (const p of patternList) {
|
|
171
173
|
if (!p.key || !p.dimension || !p.summary) continue;
|
|
172
|
-
await patterns.
|
|
173
|
-
|
|
174
|
+
const existing = await patterns.get(p.key).catch(() => null);
|
|
175
|
+
const save = existing ? patterns.incrementObservation : patterns.upsert;
|
|
176
|
+
await save(p.key, p.dimension, p.summary, p.data || {}, p.confidence || 0.5).catch(e =>
|
|
177
|
+
console.error('[analysis] Failed to save pattern:', e.message)
|
|
174
178
|
);
|
|
175
179
|
}
|
|
176
180
|
|
package/src/claude/router.js
CHANGED
|
@@ -41,6 +41,17 @@ function likelyNeedsTools(message) {
|
|
|
41
41
|
return TOOL_PATTERNS.some(p => p.test(lower));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function recentlyUsedTools(history) {
|
|
45
|
+
for (let i = history.length - 1; i >= Math.max(0, history.length - 4); i--) {
|
|
46
|
+
const msg = history[i];
|
|
47
|
+
if (msg.role === 'assistant' && Array.isArray(msg.content) &&
|
|
48
|
+
msg.content.some(b => b.type === 'tool_use')) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
async function routeMessage(client, memory, userMessage, { vlog, onRouteDecision, onRouteUpdate, recentHistory = [], selfMemory = null }) {
|
|
45
56
|
let memoryBlock = null;
|
|
46
57
|
let model = null;
|
|
@@ -83,6 +94,11 @@ If recent context shows an ongoing task (sonnet/opus was just used, multi-step w
|
|
|
83
94
|
decision.model = 'sonnet';
|
|
84
95
|
}
|
|
85
96
|
|
|
97
|
+
if (decision.model === 'haiku' && recentlyUsedTools(recentHistory)) {
|
|
98
|
+
vlog('[router] haiku overridden → sonnet (recent tool use in history)');
|
|
99
|
+
decision.model = 'sonnet';
|
|
100
|
+
}
|
|
101
|
+
|
|
86
102
|
vlog(`[router] model=${decision.model || 'sonnet'} memory=${decision.need_memory || false}${queries.length ? ` queries=${JSON.stringify(queries)}` : ''}`);
|
|
87
103
|
|
|
88
104
|
onRouteDecision?.({
|