tuna-agent 0.1.42 → 0.1.44
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/dist/agents/claude-code-adapter.js +8 -5
- package/dist/mcp/setup.d.ts +3 -2
- package/dist/mcp/setup.js +41 -16
- package/package.json +1 -1
|
@@ -107,10 +107,12 @@ export class ClaudeCodeAdapter {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
// Pre-task Memory Recall: search Mem0 for relevant past learnings
|
|
110
|
+
const defaultWorkspaceForRecall = path.join(os.homedir(), 'tuna-workspace');
|
|
111
|
+
const recallAgentName = path.basename(task.repoPath || defaultWorkspaceForRecall);
|
|
110
112
|
if (process.env.MEM0_SSH_HOST && task.description.length >= 20) {
|
|
111
113
|
try {
|
|
112
114
|
const { callMem0SearchMemory } = await import('../mcp/setup.js');
|
|
113
|
-
const memories = await callMem0SearchMemory(task.description,
|
|
115
|
+
const memories = await callMem0SearchMemory(task.description, recallAgentName, 5);
|
|
114
116
|
if (memories.length > 0) {
|
|
115
117
|
const memoryContext = memories.map(m => `- ${m}`).join('\n');
|
|
116
118
|
userMessage = `${task.description}\n\n<past_learnings>\nRelevant lessons from previous tasks:\n${memoryContext}\n</past_learnings>`;
|
|
@@ -747,8 +749,9 @@ export class ClaudeCodeAdapter {
|
|
|
747
749
|
return;
|
|
748
750
|
}
|
|
749
751
|
// Step 2: Store the AI-generated reflection in Mem0
|
|
752
|
+
const agentFolderNameForReflection = path.basename(cwd);
|
|
750
753
|
console.log(`[Reflection] Storing: "${aiReflection.substring(0, 100)}..."`);
|
|
751
|
-
await callMem0AddMemory(aiReflection,
|
|
754
|
+
await callMem0AddMemory(aiReflection, agentFolderNameForReflection);
|
|
752
755
|
this.metrics.reflectionCount++;
|
|
753
756
|
this.metrics.memoryCount++;
|
|
754
757
|
this.metrics.lastReflectionAt = new Date().toISOString();
|
|
@@ -762,7 +765,7 @@ export class ClaudeCodeAdapter {
|
|
|
762
765
|
? `Task failed: "${task.description.substring(0, 150)}". Error: ${resultSummary.substring(0, 200)}`
|
|
763
766
|
: `Task completed: "${task.description.substring(0, 150)}". Result: ${resultSummary.substring(0, 200)}`;
|
|
764
767
|
const { callMem0AddMemory } = await import('../mcp/setup.js');
|
|
765
|
-
await callMem0AddMemory(fallback,
|
|
768
|
+
await callMem0AddMemory(fallback, path.basename(cwd));
|
|
766
769
|
}
|
|
767
770
|
catch {
|
|
768
771
|
// Both AI and fallback failed — give up silently
|
|
@@ -785,7 +788,7 @@ export class ClaudeCodeAdapter {
|
|
|
785
788
|
try {
|
|
786
789
|
console.log(`[Rating→Mem0] Storing rating for task "${data.taskTitle}" (${data.score > 0 ? '👍' : '👎'})`);
|
|
787
790
|
const { callMem0AddMemory } = await import('../mcp/setup.js');
|
|
788
|
-
await callMem0AddMemory(memoryText,
|
|
791
|
+
await callMem0AddMemory(memoryText, path.basename(data.cwd));
|
|
789
792
|
console.log(`[Rating→Mem0] Rating stored successfully`);
|
|
790
793
|
}
|
|
791
794
|
catch (err) {
|
|
@@ -805,7 +808,7 @@ export class ClaudeCodeAdapter {
|
|
|
805
808
|
try {
|
|
806
809
|
console.log(`[Self-Improve] Running pattern detection (every ${ClaudeCodeAdapter.PATTERN_CHECK_INTERVAL} tasks, count=${this.taskCount})`);
|
|
807
810
|
const { callMem0Patterns } = await import('../mcp/setup.js');
|
|
808
|
-
const patterns = await callMem0Patterns(
|
|
811
|
+
const patterns = await callMem0Patterns(path.basename(cwd), 3);
|
|
809
812
|
if (patterns.length === 0) {
|
|
810
813
|
console.log(`[Self-Improve] No patterns detected yet`);
|
|
811
814
|
return;
|
package/dist/mcp/setup.d.ts
CHANGED
|
@@ -6,8 +6,9 @@ import type { AgentConfig } from '../types/index.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function fetchMem0Count(agentName: string): Promise<number>;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Add memory via OpenMemory HTTP API (POST /api/v1/memories/).
|
|
10
|
+
* Uses MEM0_HTTP_BASE if available, falls back to SSH+mem0-add.
|
|
11
|
+
* OpenMemory API stores in both SQLite (metadata) + Qdrant (vectors) — correct path.
|
|
11
12
|
*/
|
|
12
13
|
export declare function callMem0AddMemory(text: string, agentName: string): Promise<void>;
|
|
13
14
|
/**
|
package/dist/mcp/setup.js
CHANGED
|
@@ -44,32 +44,63 @@ export async function fetchMem0Count(agentName) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
47
|
+
* Add memory via OpenMemory HTTP API (POST /api/v1/memories/).
|
|
48
|
+
* Uses MEM0_HTTP_BASE if available, falls back to SSH+mem0-add.
|
|
49
|
+
* OpenMemory API stores in both SQLite (metadata) + Qdrant (vectors) — correct path.
|
|
49
50
|
*/
|
|
50
51
|
export async function callMem0AddMemory(text, agentName) {
|
|
52
|
+
if (!MEM0_SSH_HOST && !MEM0_HTTP_BASE)
|
|
53
|
+
throw new Error('Mem0 not configured');
|
|
54
|
+
const safeAgentName = agentName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'agent';
|
|
55
|
+
// Prefer HTTP API (OpenMemory) — stores in SQLite+Qdrant, shows up in counts
|
|
56
|
+
if (MEM0_HTTP_BASE) {
|
|
57
|
+
const url = `${MEM0_HTTP_BASE}/api/v1/memories/`;
|
|
58
|
+
const body = JSON.stringify({ user_id: safeAgentName, text, app: 'tuna-agent' });
|
|
59
|
+
const res = await fetch(url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body,
|
|
63
|
+
signal: AbortSignal.timeout(30000),
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
const errText = await res.text().catch(() => '');
|
|
67
|
+
throw new Error(`Mem0 HTTP add failed: ${res.status} ${errText.substring(0, 200)}`);
|
|
68
|
+
}
|
|
69
|
+
const data = await res.json();
|
|
70
|
+
if (data.error)
|
|
71
|
+
throw new Error(`Mem0 add failed: ${data.error}`);
|
|
72
|
+
const results = data.results || [];
|
|
73
|
+
const added = results.filter((r) => r.event === 'ADD' || r.event === 'UPDATE');
|
|
74
|
+
if (added.length === 0 && results.length > 0) {
|
|
75
|
+
console.log(`[Mem0] Memory deduplicated (${results.length} existing matches)`);
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Fallback: SSH + curl to OpenMemory API on remote host (127.0.0.1:8765)
|
|
51
80
|
if (!MEM0_SSH_HOST)
|
|
52
81
|
throw new Error('MEM0_SSH_HOST not configured');
|
|
53
82
|
const { execFile } = await import('child_process');
|
|
54
|
-
const safeAgentName = agentName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'agent';
|
|
55
83
|
return new Promise((resolve, reject) => {
|
|
56
84
|
const timer = setTimeout(() => reject(new Error('Mem0 call timed out')), 30000);
|
|
57
85
|
let cmd;
|
|
58
86
|
let args;
|
|
59
87
|
let options = {};
|
|
88
|
+
// JSON-safe payload
|
|
89
|
+
const payload = JSON.stringify({ user_id: safeAgentName, text, app: 'tuna-agent' });
|
|
90
|
+
const escapedPayload = payload.replace(/'/g, "'\\''");
|
|
60
91
|
if (MEM0_SSH_HOST === 'local') {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
92
|
+
// Local mode: curl to local OpenMemory API
|
|
93
|
+
cmd = 'curl';
|
|
94
|
+
args = ['-s', '-X', 'POST', 'http://127.0.0.1:8765/api/v1/memories/',
|
|
95
|
+
'-H', 'Content-Type: application/json', '-d', payload];
|
|
64
96
|
}
|
|
65
97
|
else {
|
|
98
|
+
// Remote mode: SSH then curl to localhost on remote
|
|
66
99
|
cmd = 'ssh';
|
|
67
100
|
args = ['-p', MEM0_SSH_PORT, '-o', 'StrictHostKeyChecking=no'];
|
|
68
101
|
if (MEM0_SSH_KEY)
|
|
69
102
|
args.push('-i', MEM0_SSH_KEY);
|
|
70
|
-
|
|
71
|
-
const escapedText = text.replace(/'/g, "'\\''");
|
|
72
|
-
args.push(MEM0_SSH_HOST, `MEM0_USER_ID=${safeAgentName} mem0-add '${escapedText}'`);
|
|
103
|
+
args.push(MEM0_SSH_HOST, `curl -s -X POST http://127.0.0.1:8765/api/v1/memories/ -H 'Content-Type: application/json' -d '${escapedPayload}'`);
|
|
73
104
|
}
|
|
74
105
|
execFile(cmd, args, { ...options, timeout: 30000 }, (err, stdout, stderr) => {
|
|
75
106
|
clearTimeout(timer);
|
|
@@ -77,21 +108,15 @@ export async function callMem0AddMemory(text, agentName) {
|
|
|
77
108
|
reject(new Error(`Mem0 add failed: ${err.message}${stderr ? ` stderr: ${stderr.substring(0, 200)}` : ''}`));
|
|
78
109
|
return;
|
|
79
110
|
}
|
|
80
|
-
// Check response for actual results
|
|
81
111
|
try {
|
|
82
112
|
const data = JSON.parse(stdout.trim());
|
|
83
113
|
if (data.error) {
|
|
84
114
|
reject(new Error(`Mem0 add failed: ${data.error}`));
|
|
85
115
|
return;
|
|
86
116
|
}
|
|
87
|
-
|
|
88
|
-
const added = results.filter((r) => r.event === 'ADD' || r.event === 'UPDATE');
|
|
89
|
-
if (added.length === 0 && results.length > 0) {
|
|
90
|
-
console.log(`[Mem0] Memory deduplicated (${results.length} existing matches)`);
|
|
91
|
-
}
|
|
117
|
+
// OpenMemory POST returns single object (not results array) — success if we get an id
|
|
92
118
|
}
|
|
93
119
|
catch {
|
|
94
|
-
// Non-JSON output — might be OK if exit code was 0
|
|
95
120
|
console.warn(`[Mem0] Unexpected output: ${stdout.substring(0, 100)}`);
|
|
96
121
|
}
|
|
97
122
|
resolve();
|