tuna-agent 0.1.24 → 0.1.26
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.
|
@@ -5,6 +5,8 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
5
5
|
readonly type: AgentType;
|
|
6
6
|
readonly displayName = "Claude Code";
|
|
7
7
|
private readonly agentConfig;
|
|
8
|
+
private taskCount;
|
|
9
|
+
private static readonly PATTERN_CHECK_INTERVAL;
|
|
8
10
|
constructor(config: AgentConfig);
|
|
9
11
|
checkHealth(): Promise<{
|
|
10
12
|
ok: boolean;
|
|
@@ -26,5 +28,10 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
26
28
|
comment?: string;
|
|
27
29
|
cwd: string;
|
|
28
30
|
}): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Self-Improvement: detect patterns from Mem0 and update CLAUDE.md.
|
|
33
|
+
* Runs every N tasks to evolve the agent's permanent knowledge.
|
|
34
|
+
*/
|
|
35
|
+
runSelfImprovement(cwd: string): Promise<void>;
|
|
29
36
|
dispose(): Promise<void>;
|
|
30
37
|
}
|
|
@@ -12,6 +12,8 @@ export class ClaudeCodeAdapter {
|
|
|
12
12
|
type = 'claude-code';
|
|
13
13
|
displayName = 'Claude Code';
|
|
14
14
|
agentConfig;
|
|
15
|
+
taskCount = 0;
|
|
16
|
+
static PATTERN_CHECK_INTERVAL = 5; // Check patterns every N tasks
|
|
15
17
|
constructor(config) {
|
|
16
18
|
this.agentConfig = config;
|
|
17
19
|
}
|
|
@@ -206,6 +208,9 @@ export class ClaudeCodeAdapter {
|
|
|
206
208
|
}
|
|
207
209
|
// Track last output for reflection
|
|
208
210
|
lastTaskOutput = turnAccumulatedText.trim();
|
|
211
|
+
if (lastTaskOutput) {
|
|
212
|
+
console.log(`[Reflection] Captured ${lastTaskOutput.length} chars of task output for reflection`);
|
|
213
|
+
}
|
|
209
214
|
// Send finalized message for the last turn's remaining text
|
|
210
215
|
if (turnAccumulatedText.trim()) {
|
|
211
216
|
ws.sendPMMessage(task.id, {
|
|
@@ -252,7 +257,9 @@ export class ClaudeCodeAdapter {
|
|
|
252
257
|
console.log(`[ClaudeCode] No follow-up after ${FOLLOW_UP_TIMEOUT_MS / 1000}s — closing task`);
|
|
253
258
|
pendingInputResolvers.delete(task.id);
|
|
254
259
|
const timeoutOutput = lastTaskOutput || 'Task completed (no follow-up)';
|
|
255
|
-
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
260
|
+
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
261
|
+
.then(() => this.runSelfImprovement(task.repoPath))
|
|
262
|
+
.catch(() => { });
|
|
256
263
|
return;
|
|
257
264
|
}
|
|
258
265
|
throw err;
|
|
@@ -284,7 +291,9 @@ export class ClaudeCodeAdapter {
|
|
|
284
291
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
285
292
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
286
293
|
// Post-task reflection with actual output (non-blocking)
|
|
287
|
-
this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
|
|
294
|
+
this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
|
|
295
|
+
.then(() => this.runSelfImprovement(task.repoPath))
|
|
296
|
+
.catch(() => { });
|
|
288
297
|
}
|
|
289
298
|
finally {
|
|
290
299
|
cleanupAttachments(task.id);
|
|
@@ -677,7 +686,7 @@ export class ClaudeCodeAdapter {
|
|
|
677
686
|
return;
|
|
678
687
|
try {
|
|
679
688
|
// Step 1: Generate AI-powered reflection via Ollama
|
|
680
|
-
console.log(`[Reflection] Generating AI reflection for task ${task.id} (${status})
|
|
689
|
+
console.log(`[Reflection] Generating AI reflection for task ${task.id} (${status}), input: ${resultSummary.substring(0, 150)}...`);
|
|
681
690
|
const { callMem0Reflect, callMem0AddMemory } = await import('../mcp/setup.js');
|
|
682
691
|
const aiReflection = await callMem0Reflect(task.description, resultSummary, status);
|
|
683
692
|
if (!aiReflection) {
|
|
@@ -727,6 +736,60 @@ export class ClaudeCodeAdapter {
|
|
|
727
736
|
console.warn(`[Rating→Mem0] Failed:`, err instanceof Error ? err.message : err);
|
|
728
737
|
}
|
|
729
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Self-Improvement: detect patterns from Mem0 and update CLAUDE.md.
|
|
741
|
+
* Runs every N tasks to evolve the agent's permanent knowledge.
|
|
742
|
+
*/
|
|
743
|
+
async runSelfImprovement(cwd) {
|
|
744
|
+
if (!process.env.MEM0_SSH_HOST)
|
|
745
|
+
return;
|
|
746
|
+
this.taskCount++;
|
|
747
|
+
if (this.taskCount % ClaudeCodeAdapter.PATTERN_CHECK_INTERVAL !== 0)
|
|
748
|
+
return;
|
|
749
|
+
try {
|
|
750
|
+
console.log(`[Self-Improve] Running pattern detection (every ${ClaudeCodeAdapter.PATTERN_CHECK_INTERVAL} tasks, count=${this.taskCount})`);
|
|
751
|
+
const { callMem0Patterns } = await import('../mcp/setup.js');
|
|
752
|
+
const patterns = await callMem0Patterns(this.agentConfig.name, 3);
|
|
753
|
+
if (patterns.length === 0) {
|
|
754
|
+
console.log(`[Self-Improve] No patterns detected yet`);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
// Read current CLAUDE.md to check for existing rules
|
|
758
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
759
|
+
let existingContent = '';
|
|
760
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
761
|
+
existingContent = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
762
|
+
}
|
|
763
|
+
// Filter out patterns that are already in CLAUDE.md
|
|
764
|
+
const newPatterns = patterns.filter(p => {
|
|
765
|
+
// Simple dedup: check if rule text (or close match) already exists
|
|
766
|
+
const ruleNormalized = p.rule.toLowerCase().replace(/[^a-z0-9\s]/g, '');
|
|
767
|
+
return !existingContent.toLowerCase().includes(ruleNormalized.substring(0, 50));
|
|
768
|
+
});
|
|
769
|
+
if (newPatterns.length === 0) {
|
|
770
|
+
console.log(`[Self-Improve] ${patterns.length} patterns found but all already in CLAUDE.md`);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
// Append new rules to CLAUDE.md under "## Learned Rules"
|
|
774
|
+
const SECTION_HEADER = '## Learned Rules';
|
|
775
|
+
const rulesBlock = newPatterns.map(p => `- ${p.rule} (confidence: ${p.confidence})`).join('\n');
|
|
776
|
+
if (existingContent.includes(SECTION_HEADER)) {
|
|
777
|
+
// Append to existing section
|
|
778
|
+
const updatedContent = existingContent.replace(SECTION_HEADER, `${SECTION_HEADER}\n${rulesBlock}`);
|
|
779
|
+
fs.writeFileSync(claudeMdPath, updatedContent);
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
// Add new section at the end
|
|
783
|
+
const separator = existingContent.endsWith('\n') ? '\n' : '\n\n';
|
|
784
|
+
fs.writeFileSync(claudeMdPath, existingContent + separator + `${SECTION_HEADER}\n${rulesBlock}\n`);
|
|
785
|
+
}
|
|
786
|
+
console.log(`[Self-Improve] Added ${newPatterns.length} new rules to CLAUDE.md:`);
|
|
787
|
+
newPatterns.forEach(p => console.log(`[Self-Improve] - ${p.rule}`));
|
|
788
|
+
}
|
|
789
|
+
catch (err) {
|
|
790
|
+
console.warn(`[Self-Improve] Failed:`, err instanceof Error ? err.message : err);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
730
793
|
async dispose() {
|
|
731
794
|
// No persistent resources to clean up
|
|
732
795
|
}
|
package/dist/mcp/setup.d.ts
CHANGED
|
@@ -9,6 +9,15 @@ export declare function callMem0AddMemory(text: string, agentName: string): Prom
|
|
|
9
9
|
* Returns array of memory texts sorted by relevance.
|
|
10
10
|
*/
|
|
11
11
|
export declare function callMem0SearchMemory(query: string, agentName: string, limit?: number): Promise<string[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Detect patterns from accumulated memories via mem0-patterns script.
|
|
14
|
+
* Returns synthesized rules from clusters of similar memories.
|
|
15
|
+
*/
|
|
16
|
+
export declare function callMem0Patterns(agentName: string, minCluster?: number): Promise<Array<{
|
|
17
|
+
rule: string;
|
|
18
|
+
confidence: number;
|
|
19
|
+
sources: string[];
|
|
20
|
+
}>>;
|
|
12
21
|
/**
|
|
13
22
|
* Generate AI-powered reflection from task results using Ollama.
|
|
14
23
|
* Calls mem0-reflect script on relabs01 via SSH.
|
package/dist/mcp/setup.js
CHANGED
|
@@ -124,6 +124,51 @@ export async function callMem0SearchMemory(query, agentName, limit = 5) {
|
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Detect patterns from accumulated memories via mem0-patterns script.
|
|
129
|
+
* Returns synthesized rules from clusters of similar memories.
|
|
130
|
+
*/
|
|
131
|
+
export async function callMem0Patterns(agentName, minCluster = 3) {
|
|
132
|
+
if (!MEM0_SSH_HOST)
|
|
133
|
+
return [];
|
|
134
|
+
const { execFile } = await import('child_process');
|
|
135
|
+
const safeAgentName = agentName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'agent';
|
|
136
|
+
const input = JSON.stringify({ user_id: safeAgentName, min_cluster: minCluster });
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
let cmd;
|
|
139
|
+
let args;
|
|
140
|
+
let options = {};
|
|
141
|
+
if (MEM0_SSH_HOST === 'local') {
|
|
142
|
+
cmd = 'mem0-patterns';
|
|
143
|
+
args = [];
|
|
144
|
+
options.env = { ...process.env };
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
cmd = 'ssh';
|
|
148
|
+
args = ['-p', MEM0_SSH_PORT, '-o', 'StrictHostKeyChecking=no'];
|
|
149
|
+
if (MEM0_SSH_KEY)
|
|
150
|
+
args.push('-i', MEM0_SSH_KEY);
|
|
151
|
+
args.push(MEM0_SSH_HOST, 'mem0-patterns');
|
|
152
|
+
}
|
|
153
|
+
const child = execFile(cmd, args, { ...options, timeout: 120000 }, (err, stdout) => {
|
|
154
|
+
if (err) {
|
|
155
|
+
console.warn(`[Mem0 Patterns] Failed: ${err.message}`);
|
|
156
|
+
resolve([]);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const data = JSON.parse(stdout.trim());
|
|
161
|
+
console.log(`[Mem0 Patterns] ${data.total_memories} memories, ${data.clusters} clusters, ${(data.patterns || []).length} patterns found`);
|
|
162
|
+
resolve(data.patterns || []);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
resolve([]);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
child.stdin?.write(input);
|
|
169
|
+
child.stdin?.end();
|
|
170
|
+
});
|
|
171
|
+
}
|
|
127
172
|
/**
|
|
128
173
|
* Generate AI-powered reflection from task results using Ollama.
|
|
129
174
|
* Calls mem0-reflect script on relabs01 via SSH.
|