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
- // Set current agent context (metrics + Mem0 identity)
153
- this.currentAgentId = task.agentId || '';
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
- this.currentAgentName = path.basename(task.repoPath || defaultWorkspaceEarly);
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(this.currentAgentId, cwd);
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, this.currentAgentName, 5);
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(this.currentAgentName).then(count => {
211
- if (count > this.metrics.memoryCount) {
212
- this.metrics.memoryCount = count;
213
- console.log(`[Metrics] Seeded memoryCount=${count} from Mem0 for "${this.currentAgentName}"`);
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.learnedRulesMap.set(this.currentAgentId, rules);
1048
- this.metrics.rulesCount = rules.length;
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.taskCount++;
1066
+ trackMetrics(status, durationMs, agentId) {
1067
+ const m = agentId ? this.getMetricsForAgent(agentId) : this.metrics;
1068
+ m.taskCount++;
1063
1069
  if (status === 'done')
1064
- this.metrics.successCount++;
1070
+ m.successCount++;
1065
1071
  else
1066
- this.metrics.failCount++;
1067
- this.metrics.totalDurationMs += durationMs;
1068
- this.metrics.avgDurationMs = Math.round(this.metrics.totalDurationMs / this.metrics.taskCount);
1069
- this.metrics.lastTaskAt = new Date().toISOString();
1070
- console.log(`[Metrics] Tasks: ${this.metrics.successCount}✓ ${this.metrics.failCount}✗ | Avg: ${(this.metrics.avgDurationMs / 1000).toFixed(0)}s | Reflections: ${this.metrics.reflectionCount} | Patterns: ${this.metrics.patternsLearnedCount}`);
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
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.71",
3
+ "version": "0.1.72",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"