tuna-agent 0.1.71 → 0.1.72
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.
|
@@ -79,14 +79,14 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
79
79
|
private static extractKeyPhrases;
|
|
80
80
|
/** Check if two rules are semantically similar (>40% key phrase overlap). */
|
|
81
81
|
private static isSimilarRule;
|
|
82
|
-
runSelfImprovement(cwd: string): Promise<void>;
|
|
82
|
+
runSelfImprovement(cwd: string, agentId?: string): Promise<void>;
|
|
83
83
|
/**
|
|
84
84
|
* Parse "## Learned Rules" section from CLAUDE.md and store in learnedRulesMap.
|
|
85
85
|
* Rule format: `- Rule text (confidence: 0.7)`
|
|
86
86
|
*/
|
|
87
87
|
private parseLearnedRules;
|
|
88
88
|
/** Track task completion metrics (public for daemon resume path). */
|
|
89
|
-
trackMetricsPublic(status: 'done' | 'failed', durationMs: number): void;
|
|
89
|
+
trackMetricsPublic(status: 'done' | 'failed', durationMs: number, agentId?: string): void;
|
|
90
90
|
/** Track task completion metrics. */
|
|
91
91
|
private trackMetrics;
|
|
92
92
|
dispose(): Promise<void>;
|
|
@@ -149,13 +149,16 @@ export class ClaudeCodeAdapter {
|
|
|
149
149
|
: undefined;
|
|
150
150
|
// Default mode: direct chat with Claude CLI (no PM layer)
|
|
151
151
|
// Only use PM planning when mode is explicitly 'tuna'
|
|
152
|
-
//
|
|
153
|
-
|
|
152
|
+
// Capture agent context as LOCAL variables for parallel-safety
|
|
153
|
+
const localAgentId = task.agentId || '';
|
|
154
154
|
const defaultWorkspaceEarly = path.join(os.homedir(), 'tuna-workspace');
|
|
155
|
-
|
|
155
|
+
const localAgentName = path.basename(task.repoPath || defaultWorkspaceEarly);
|
|
156
|
+
// Also set instance vars for backward compat (metrics getter, heartbeat, etc.)
|
|
157
|
+
this.currentAgentId = localAgentId;
|
|
158
|
+
this.currentAgentName = localAgentName;
|
|
156
159
|
// Track agent folder for rules parsing in heartbeat
|
|
157
160
|
const cwd = task.repoPath || defaultWorkspaceEarly;
|
|
158
|
-
this.agentFolderMap.set(
|
|
161
|
+
this.agentFolderMap.set(localAgentId, cwd);
|
|
159
162
|
if (task.mode !== 'tuna') {
|
|
160
163
|
console.log(`[ClaudeCode] Agent Team mode — direct chat with Claude CLI`);
|
|
161
164
|
ws.sendProgress(task.id, 'executing', { startedAt: new Date().toISOString() });
|
|
@@ -179,7 +182,7 @@ export class ClaudeCodeAdapter {
|
|
|
179
182
|
if (process.env.MEM0_SSH_HOST && task.description.length >= 20) {
|
|
180
183
|
try {
|
|
181
184
|
const { callMem0SearchMemory } = await import('../mcp/setup.js');
|
|
182
|
-
const memories = await callMem0SearchMemory(task.description,
|
|
185
|
+
const memories = await callMem0SearchMemory(task.description, localAgentName, 5);
|
|
183
186
|
if (memories.length > 0) {
|
|
184
187
|
const memoryContext = memories.map(m => `- ${m}`).join('\n');
|
|
185
188
|
userMessage = `${task.description}\n\n<past_learnings>\nRelevant lessons from previous tasks:\n${memoryContext}\n</past_learnings>`;
|
|
@@ -207,10 +210,11 @@ export class ClaudeCodeAdapter {
|
|
|
207
210
|
if (round === 0) {
|
|
208
211
|
writeAgentFolderMcpConfig(cwd, this.agentConfig);
|
|
209
212
|
// Seed memoryCount from Mem0 so it survives daemon restarts (non-blocking)
|
|
210
|
-
fetchMem0Count(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
fetchMem0Count(localAgentName).then(count => {
|
|
214
|
+
const m = this.getMetricsForAgent(localAgentId);
|
|
215
|
+
if (count > m.memoryCount) {
|
|
216
|
+
m.memoryCount = count;
|
|
217
|
+
console.log(`[Metrics] Seeded memoryCount=${count} from Mem0 for "${localAgentName}"`);
|
|
214
218
|
}
|
|
215
219
|
}).catch(() => { });
|
|
216
220
|
}
|
|
@@ -328,7 +332,7 @@ export class ClaudeCodeAdapter {
|
|
|
328
332
|
startedAt: firstChunkIso || undefined,
|
|
329
333
|
});
|
|
330
334
|
ws.sendTaskFailed(task.id, result.result);
|
|
331
|
-
this.trackMetrics('failed', totalDurationMs);
|
|
335
|
+
this.trackMetrics('failed', totalDurationMs, localAgentId);
|
|
332
336
|
console.log(`[ClaudeCode] Agent Team task ${task.id} failed in round ${round + 1}`);
|
|
333
337
|
this.runReflection(task, result.result, 'failed', cwd).catch(() => { });
|
|
334
338
|
return;
|
|
@@ -389,10 +393,10 @@ export class ClaudeCodeAdapter {
|
|
|
389
393
|
durationMs: totalDurationMs,
|
|
390
394
|
sessionId,
|
|
391
395
|
});
|
|
392
|
-
this.trackMetrics('done', totalDurationMs);
|
|
396
|
+
this.trackMetrics('done', totalDurationMs, localAgentId);
|
|
393
397
|
const timeoutOutput = lastTaskOutput || 'Task completed (no follow-up)';
|
|
394
398
|
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
395
|
-
.then(() => this.runSelfImprovement(task.repoPath))
|
|
399
|
+
.then(() => this.runSelfImprovement(task.repoPath, task.agentId))
|
|
396
400
|
.catch(() => { });
|
|
397
401
|
return;
|
|
398
402
|
}
|
|
@@ -423,11 +427,11 @@ export class ClaudeCodeAdapter {
|
|
|
423
427
|
});
|
|
424
428
|
await new Promise(resolve => setTimeout(resolve, 150));
|
|
425
429
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
426
|
-
this.trackMetrics('done', totalDurationMs);
|
|
430
|
+
this.trackMetrics('done', totalDurationMs, localAgentId);
|
|
427
431
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
428
432
|
// Post-task reflection with actual output (non-blocking)
|
|
429
433
|
this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
|
|
430
|
-
.then(() => this.runSelfImprovement(task.repoPath))
|
|
434
|
+
.then(() => this.runSelfImprovement(task.repoPath, task.agentId))
|
|
431
435
|
.catch(() => { });
|
|
432
436
|
}
|
|
433
437
|
finally {
|
|
@@ -903,7 +907,7 @@ export class ClaudeCodeAdapter {
|
|
|
903
907
|
const overlap = phrasesA.filter(p => phrasesB.has(p)).length;
|
|
904
908
|
return overlap / phrasesA.length > 0.4;
|
|
905
909
|
}
|
|
906
|
-
async runSelfImprovement(cwd) {
|
|
910
|
+
async runSelfImprovement(cwd, agentId) {
|
|
907
911
|
if (!process.env.MEM0_SSH_HOST)
|
|
908
912
|
return;
|
|
909
913
|
this.taskCount++;
|
|
@@ -989,7 +993,7 @@ export class ClaudeCodeAdapter {
|
|
|
989
993
|
console.log(`[Self-Improve] Added ${toAdd.length} new rules to CLAUDE.md:`);
|
|
990
994
|
toAdd.forEach(p => console.log(`[Self-Improve] - ${p.rule}`));
|
|
991
995
|
// Sync learned rules to heartbeat metrics
|
|
992
|
-
this.parseLearnedRules(claudeMdPath);
|
|
996
|
+
this.parseLearnedRules(claudeMdPath, agentId);
|
|
993
997
|
}
|
|
994
998
|
catch (err) {
|
|
995
999
|
console.warn(`[Self-Improve] Failed:`, err instanceof Error ? err.message : err);
|
|
@@ -999,7 +1003,7 @@ export class ClaudeCodeAdapter {
|
|
|
999
1003
|
* Parse "## Learned Rules" section from CLAUDE.md and store in learnedRulesMap.
|
|
1000
1004
|
* Rule format: `- Rule text (confidence: 0.7)`
|
|
1001
1005
|
*/
|
|
1002
|
-
parseLearnedRules(claudeMdPath) {
|
|
1006
|
+
parseLearnedRules(claudeMdPath, agentId) {
|
|
1003
1007
|
try {
|
|
1004
1008
|
if (!fs.existsSync(claudeMdPath))
|
|
1005
1009
|
return;
|
|
@@ -1044,8 +1048,9 @@ export class ClaudeCodeAdapter {
|
|
|
1044
1048
|
});
|
|
1045
1049
|
}
|
|
1046
1050
|
if (rules.length > 0) {
|
|
1047
|
-
this.
|
|
1048
|
-
this.
|
|
1051
|
+
const resolvedAgentId = agentId || this.currentAgentId;
|
|
1052
|
+
this.learnedRulesMap.set(resolvedAgentId, rules);
|
|
1053
|
+
this.getMetricsForAgent(resolvedAgentId).rulesCount = rules.length;
|
|
1049
1054
|
console.log(`[Self-Improve] Parsed ${rules.length} rules from CLAUDE.md for heartbeat sync`);
|
|
1050
1055
|
}
|
|
1051
1056
|
}
|
|
@@ -1054,20 +1059,21 @@ export class ClaudeCodeAdapter {
|
|
|
1054
1059
|
}
|
|
1055
1060
|
}
|
|
1056
1061
|
/** Track task completion metrics (public for daemon resume path). */
|
|
1057
|
-
trackMetricsPublic(status, durationMs) {
|
|
1058
|
-
this.trackMetrics(status, durationMs);
|
|
1062
|
+
trackMetricsPublic(status, durationMs, agentId) {
|
|
1063
|
+
this.trackMetrics(status, durationMs, agentId);
|
|
1059
1064
|
}
|
|
1060
1065
|
/** Track task completion metrics. */
|
|
1061
|
-
trackMetrics(status, durationMs) {
|
|
1062
|
-
this.metrics
|
|
1066
|
+
trackMetrics(status, durationMs, agentId) {
|
|
1067
|
+
const m = agentId ? this.getMetricsForAgent(agentId) : this.metrics;
|
|
1068
|
+
m.taskCount++;
|
|
1063
1069
|
if (status === 'done')
|
|
1064
|
-
|
|
1070
|
+
m.successCount++;
|
|
1065
1071
|
else
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
console.log(`[Metrics] Tasks: ${
|
|
1072
|
+
m.failCount++;
|
|
1073
|
+
m.totalDurationMs += durationMs;
|
|
1074
|
+
m.avgDurationMs = Math.round(m.totalDurationMs / m.taskCount);
|
|
1075
|
+
m.lastTaskAt = new Date().toISOString();
|
|
1076
|
+
console.log(`[Metrics] Tasks: ${m.successCount}✓ ${m.failCount}✗ | Avg: ${(m.avgDurationMs / 1000).toFixed(0)}s | Reflections: ${m.reflectionCount} | Patterns: ${m.patternsLearnedCount}`);
|
|
1071
1077
|
}
|
|
1072
1078
|
async dispose() {
|
|
1073
1079
|
// No persistent resources to clean up
|
package/dist/daemon/index.js
CHANGED
|
@@ -867,7 +867,7 @@ ${skillContent.slice(0, 15000)}`;
|
|
|
867
867
|
// Track metrics + reflection on the adapter
|
|
868
868
|
if (adapter.type === 'claude-code') {
|
|
869
869
|
const ccAdapter = adapter;
|
|
870
|
-
ccAdapter.trackMetricsPublic('done', totalDurationMs);
|
|
870
|
+
ccAdapter.trackMetricsPublic('done', totalDurationMs, savedState.agentId);
|
|
871
871
|
ccAdapter.runReflection({ id: taskId, description: firstMessage, repoPath: cwd, enableReflection: true }, lastResumeOutput || 'Task completed (no follow-up)', 'done', cwd).then(() => ccAdapter.runSelfImprovement(cwd)).catch(() => { });
|
|
872
872
|
}
|
|
873
873
|
return;
|