tuna-agent 0.1.20 → 0.1.22

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.
@@ -68,6 +68,21 @@ export class ClaudeCodeAdapter {
68
68
  console.error(`[ClaudeCode] Failed to download initial attachments:`, err);
69
69
  }
70
70
  }
71
+ // Pre-task Memory Recall: search Mem0 for relevant past learnings
72
+ if (process.env.MEM0_SSH_HOST && task.description.length >= 20) {
73
+ try {
74
+ const { callMem0SearchMemory } = await import('../mcp/setup.js');
75
+ const memories = await callMem0SearchMemory(task.description, this.agentConfig.name, 5);
76
+ if (memories.length > 0) {
77
+ const memoryContext = memories.map(m => `- ${m}`).join('\n');
78
+ userMessage = `${task.description}\n\n<past_learnings>\nRelevant lessons from previous tasks:\n${memoryContext}\n</past_learnings>`;
79
+ console.log(`[Mem0 Recall] Injected ${memories.length} memories into task prompt`);
80
+ }
81
+ }
82
+ catch (err) {
83
+ console.warn(`[Mem0 Recall] Failed:`, err instanceof Error ? err.message : err);
84
+ }
85
+ }
71
86
  try {
72
87
  for (let round = 0; round < MAX_ROUNDS; round++) {
73
88
  let streamMsgId = `team-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
@@ -659,7 +674,18 @@ export class ClaudeCodeAdapter {
659
674
  const taskContext = task.description.substring(0, 150);
660
675
  const resultContext = resultSummary.substring(0, 200);
661
676
  const statusLabel = status === 'done' ? 'COMPLETED' : 'FAILED';
662
- const memoryText = `[TASK ${statusLabel}] "${taskContext}". Result: ${resultContext}`;
677
+ // Structured reflection: what was asked, what happened, what to remember
678
+ const parts = [];
679
+ if (status === 'failed') {
680
+ parts.push(`[TASK FAILED] "${taskContext}"`);
681
+ parts.push(`Error: ${resultContext}`);
682
+ parts.push(`Lesson: This type of task failed — review approach next time`);
683
+ }
684
+ else {
685
+ parts.push(`[TASK ${statusLabel}] "${taskContext}"`);
686
+ parts.push(`Result: ${resultContext}`);
687
+ }
688
+ const memoryText = parts.join('. ');
663
689
  try {
664
690
  console.log(`[Reflection] Storing reflection for task ${task.id} (${status})`);
665
691
  const { callMem0AddMemory } = await import('../mcp/setup.js');
@@ -4,6 +4,11 @@ import type { AgentConfig } from '../types/index.js';
4
4
  * Calls `mem0-add <text>` directly or via SSH — simple, reliable.
5
5
  */
6
6
  export declare function callMem0AddMemory(text: string, agentName: string): Promise<void>;
7
+ /**
8
+ * Search Mem0 for relevant memories before task execution.
9
+ * Returns array of memory texts sorted by relevance.
10
+ */
11
+ export declare function callMem0SearchMemory(query: string, agentName: string, limit?: number): Promise<string[]>;
7
12
  /**
8
13
  * Generate MCP server config file for Claude Code.
9
14
  * This file is auto-detected by runClaude and passed via --mcp-config.
package/dist/mcp/setup.js CHANGED
@@ -75,6 +75,55 @@ export async function callMem0AddMemory(text, agentName) {
75
75
  });
76
76
  });
77
77
  }
78
+ /**
79
+ * Search Mem0 for relevant memories before task execution.
80
+ * Returns array of memory texts sorted by relevance.
81
+ */
82
+ export async function callMem0SearchMemory(query, agentName, limit = 5) {
83
+ if (!MEM0_SSH_HOST)
84
+ return [];
85
+ const { execFile } = await import('child_process');
86
+ const safeAgentName = agentName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'agent';
87
+ return new Promise((resolve) => {
88
+ let cmd;
89
+ let args;
90
+ let options = {};
91
+ if (MEM0_SSH_HOST === 'local') {
92
+ cmd = 'mem0-search';
93
+ args = [query, String(limit)];
94
+ options.env = { ...process.env, MEM0_USER_ID: safeAgentName };
95
+ }
96
+ else {
97
+ cmd = 'ssh';
98
+ args = ['-p', MEM0_SSH_PORT, '-o', 'StrictHostKeyChecking=no'];
99
+ if (MEM0_SSH_KEY)
100
+ args.push('-i', MEM0_SSH_KEY);
101
+ const escapedQuery = query.replace(/'/g, "'\\''");
102
+ args.push(MEM0_SSH_HOST, `MEM0_USER_ID=${safeAgentName} mem0-search '${escapedQuery}' ${limit}`);
103
+ }
104
+ execFile(cmd, args, { ...options, timeout: 15000 }, (err, stdout) => {
105
+ if (err) {
106
+ console.warn(`[Mem0 Search] Failed: ${err.message}`);
107
+ resolve([]);
108
+ return;
109
+ }
110
+ try {
111
+ const data = JSON.parse(stdout.trim());
112
+ const results = (data.results || []);
113
+ // Only keep memories with relevance score >= 0.6
114
+ const relevant = results.filter(r => r.score >= 0.6);
115
+ if (relevant.length > 0) {
116
+ console.log(`[Mem0 Search] ${relevant.length}/${results.length} memories above threshold (scores: ${relevant.map(r => r.score.toFixed(2)).join(', ')})`);
117
+ relevant.forEach((r, i) => console.log(`[Mem0 Search] ${i + 1}. [${r.score.toFixed(2)}] ${r.text.substring(0, 100)}`));
118
+ }
119
+ resolve(relevant.map(r => r.text));
120
+ }
121
+ catch {
122
+ resolve([]);
123
+ }
124
+ });
125
+ });
126
+ }
78
127
  /**
79
128
  * Build Mem0 MCP server config for an agent.
80
129
  * - MEM0_SSH_HOST="local": run mem0-mcp directly (Mem0 infra on same machine)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"