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.
@@ -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, this.agentConfig.name, 5);
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, this.agentConfig.name);
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, this.agentConfig.name);
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, this.agentConfig.name);
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(this.agentConfig.name, 3);
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;
@@ -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
- * Call Mem0 add_memory via mem0-add script (bypasses OpenMemory API).
10
- * Calls `mem0-add <text>` directly or via SSH — simple, reliable.
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
- * Call Mem0 add_memory via mem0-add script (bypasses OpenMemory API).
48
- * Calls `mem0-add <text>` directly or via SSH — simple, reliable.
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
- cmd = 'mem0-add';
62
- args = [text];
63
- options.env = { ...process.env, MEM0_USER_ID: safeAgentName };
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
- // Shell-escape text with single quotes (escape internal single quotes)
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
- const results = data.results || [];
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"