tuna-agent 0.1.25 → 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
|
}
|
|
@@ -255,7 +257,9 @@ export class ClaudeCodeAdapter {
|
|
|
255
257
|
console.log(`[ClaudeCode] No follow-up after ${FOLLOW_UP_TIMEOUT_MS / 1000}s — closing task`);
|
|
256
258
|
pendingInputResolvers.delete(task.id);
|
|
257
259
|
const timeoutOutput = lastTaskOutput || 'Task completed (no follow-up)';
|
|
258
|
-
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
260
|
+
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
261
|
+
.then(() => this.runSelfImprovement(task.repoPath))
|
|
262
|
+
.catch(() => { });
|
|
259
263
|
return;
|
|
260
264
|
}
|
|
261
265
|
throw err;
|
|
@@ -287,7 +291,9 @@ export class ClaudeCodeAdapter {
|
|
|
287
291
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
288
292
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
289
293
|
// Post-task reflection with actual output (non-blocking)
|
|
290
|
-
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(() => { });
|
|
291
297
|
}
|
|
292
298
|
finally {
|
|
293
299
|
cleanupAttachments(task.id);
|
|
@@ -730,6 +736,60 @@ export class ClaudeCodeAdapter {
|
|
|
730
736
|
console.warn(`[Rating→Mem0] Failed:`, err instanceof Error ? err.message : err);
|
|
731
737
|
}
|
|
732
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
|
+
}
|
|
733
793
|
async dispose() {
|
|
734
794
|
// No persistent resources to clean up
|
|
735
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.
|