tuna-agent 0.1.73 → 0.1.75

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.
@@ -33,11 +33,6 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
33
33
  private metricsMap;
34
34
  private learnedRulesMap;
35
35
  private agentFolderMap;
36
- private currentAgentId;
37
- /** Folder basename of current agent (e.g. "co-founder"). Used as Mem0 user_id. */
38
- private currentAgentName;
39
- /** Returns metrics for the currently active agent, initializing if needed. */
40
- private get metrics();
41
36
  /** Returns metrics for a specific agent by ID (safe for parallel execution). */
42
37
  getMetricsForAgent(agentId: string): AgentMetrics;
43
38
  constructor(config: AgentConfig);
@@ -79,14 +74,14 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
79
74
  private static extractKeyPhrases;
80
75
  /** Check if two rules are semantically similar (>40% key phrase overlap). */
81
76
  private static isSimilarRule;
82
- runSelfImprovement(cwd: string, agentId?: string): Promise<void>;
77
+ runSelfImprovement(cwd: string, agentId: string): Promise<void>;
83
78
  /**
84
79
  * Parse "## Learned Rules" section from CLAUDE.md and store in learnedRulesMap.
85
80
  * Rule format: `- Rule text (confidence: 0.7)`
86
81
  */
87
82
  private parseLearnedRules;
88
83
  /** Track task completion metrics (public for daemon resume path). */
89
- trackMetricsPublic(status: 'done' | 'failed', durationMs: number, agentId?: string): void;
84
+ trackMetricsPublic(status: 'done' | 'failed', durationMs: number, agentId: string): void;
90
85
  /** Track task completion metrics. */
91
86
  private trackMetrics;
92
87
  dispose(): Promise<void>;
@@ -18,13 +18,6 @@ export class ClaudeCodeAdapter {
18
18
  metricsMap = new Map();
19
19
  learnedRulesMap = new Map();
20
20
  agentFolderMap = new Map(); // agentId -> folder path
21
- currentAgentId = '';
22
- /** Folder basename of current agent (e.g. "co-founder"). Used as Mem0 user_id. */
23
- currentAgentName = '';
24
- /** Returns metrics for the currently active agent, initializing if needed. */
25
- get metrics() {
26
- return this.getMetricsForAgent(this.currentAgentId);
27
- }
28
21
  /** Returns metrics for a specific agent by ID (safe for parallel execution). */
29
22
  getMetricsForAgent(agentId) {
30
23
  if (!this.metricsMap.has(agentId)) {
@@ -65,10 +58,7 @@ export class ClaudeCodeAdapter {
65
58
  getMetrics() {
66
59
  // Re-parse rules from CLAUDE.md for all known agents
67
60
  this.agentFolderMap.forEach((folder, agentId) => {
68
- const savedId = this.currentAgentId;
69
- this.currentAgentId = agentId;
70
- this.parseLearnedRules(ClaudeCodeAdapter.resolveClaudeMdPath(folder));
71
- this.currentAgentId = savedId;
61
+ this.parseLearnedRules(ClaudeCodeAdapter.resolveClaudeMdPath(folder), agentId);
72
62
  });
73
63
  const agentMetricsMap = {};
74
64
  // Include agents from metricsMap
@@ -103,12 +93,10 @@ export class ClaudeCodeAdapter {
103
93
  try {
104
94
  const count = await fetchMem0Count(agentName);
105
95
  if (count > 0) {
106
- const savedId = this.currentAgentId;
107
- this.currentAgentId = agentId;
108
- if (count > this.metrics.memoryCount) {
109
- this.metrics.memoryCount = count;
96
+ const m = this.getMetricsForAgent(agentId);
97
+ if (count > m.memoryCount) {
98
+ m.memoryCount = count;
110
99
  }
111
- this.currentAgentId = savedId;
112
100
  console.log(`[Metrics] Seeded memoryCount=${count} for "${agentName}"`);
113
101
  }
114
102
  }
@@ -149,13 +137,10 @@ export class ClaudeCodeAdapter {
149
137
  : undefined;
150
138
  // Default mode: direct chat with Claude CLI (no PM layer)
151
139
  // Only use PM planning when mode is explicitly 'tuna'
152
- // Capture agent context as LOCAL variables for parallel-safety
140
+ // Capture agent context as LOCAL variables no shared mutable state
153
141
  const localAgentId = task.agentId || '';
154
142
  const defaultWorkspaceEarly = path.join(os.homedir(), 'tuna-workspace');
155
143
  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;
159
144
  // Track agent folder for rules parsing in heartbeat
160
145
  const cwd = task.repoPath || defaultWorkspaceEarly;
161
146
  this.agentFolderMap.set(localAgentId, cwd);
@@ -396,7 +381,7 @@ export class ClaudeCodeAdapter {
396
381
  this.trackMetrics('done', totalDurationMs, localAgentId);
397
382
  const timeoutOutput = lastTaskOutput || 'Task completed (no follow-up)';
398
383
  this.runReflection(task, timeoutOutput, 'done', task.repoPath)
399
- .then(() => this.runSelfImprovement(task.repoPath, task.agentId))
384
+ .then(() => this.runSelfImprovement(task.repoPath, localAgentId))
400
385
  .catch(() => { });
401
386
  return;
402
387
  }
@@ -431,7 +416,7 @@ export class ClaudeCodeAdapter {
431
416
  console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
432
417
  // Post-task reflection with actual output (non-blocking)
433
418
  this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
434
- .then(() => this.runSelfImprovement(task.repoPath, task.agentId))
419
+ .then(() => this.runSelfImprovement(task.repoPath, localAgentId))
435
420
  .catch(() => { });
436
421
  }
437
422
  finally {
@@ -824,9 +809,8 @@ export class ClaudeCodeAdapter {
824
809
  const MIN_DESCRIPTION_LENGTH = 20;
825
810
  if (task.description.length < MIN_DESCRIPTION_LENGTH)
826
811
  return;
827
- // Derive agent name from cwd (safe for parallel execution — no shared state)
828
- const agentName = path.basename(cwd) || this.currentAgentName;
829
- const agentId = task.agentId || this.currentAgentId;
812
+ const agentName = path.basename(cwd);
813
+ const agentId = task.agentId || '';
830
814
  try {
831
815
  // Step 1: Generate AI-powered reflection via Ollama
832
816
  console.log(`[Reflection] Generating AI reflection for task ${task.id} (${status}), input: ${resultSummary.substring(0, 150)}...`);
@@ -877,7 +861,7 @@ export class ClaudeCodeAdapter {
877
861
  try {
878
862
  console.log(`[Rating→Mem0] Storing rating for task "${data.taskTitle}" (${data.score > 0 ? '👍' : '👎'})`);
879
863
  const { callMem0AddMemory } = await import('../mcp/setup.js');
880
- const agentName = path.basename(data.cwd) || this.currentAgentName;
864
+ const agentName = path.basename(data.cwd);
881
865
  await callMem0AddMemory(memoryText, agentName);
882
866
  console.log(`[Rating→Mem0] Rating stored successfully`);
883
867
  }
@@ -916,7 +900,7 @@ export class ClaudeCodeAdapter {
916
900
  try {
917
901
  console.log(`[Self-Improve] Running pattern detection (every ${ClaudeCodeAdapter.PATTERN_CHECK_INTERVAL} tasks, count=${this.taskCount})`);
918
902
  const { callMem0Patterns } = await import('../mcp/setup.js');
919
- const agentName = path.basename(cwd) || this.currentAgentName;
903
+ const agentName = path.basename(cwd);
920
904
  const patterns = await callMem0Patterns(agentName, 2);
921
905
  if (patterns.length === 0) {
922
906
  console.log(`[Self-Improve] No patterns detected yet`);
@@ -986,7 +970,7 @@ export class ClaudeCodeAdapter {
986
970
  const separator = existingContent.endsWith('\n') ? '\n' : '\n\n';
987
971
  fs.writeFileSync(claudeMdPath, existingContent + separator + `${SECTION_HEADER}\n${rulesBlock}\n`);
988
972
  }
989
- const m = agentId ? this.getMetricsForAgent(agentId) : this.metrics;
973
+ const m = this.getMetricsForAgent(agentId);
990
974
  m.patternsLearnedCount += toAdd.length;
991
975
  m.rulesCount += toAdd.length;
992
976
  m.lastPatternAt = new Date().toISOString();
@@ -1049,9 +1033,8 @@ export class ClaudeCodeAdapter {
1049
1033
  });
1050
1034
  }
1051
1035
  if (rules.length > 0) {
1052
- const resolvedAgentId = agentId || this.currentAgentId;
1053
- this.learnedRulesMap.set(resolvedAgentId, rules);
1054
- this.getMetricsForAgent(resolvedAgentId).rulesCount = rules.length;
1036
+ this.learnedRulesMap.set(agentId, rules);
1037
+ this.getMetricsForAgent(agentId).rulesCount = rules.length;
1055
1038
  console.log(`[Self-Improve] Parsed ${rules.length} rules from CLAUDE.md for heartbeat sync`);
1056
1039
  }
1057
1040
  }
@@ -1065,7 +1048,7 @@ export class ClaudeCodeAdapter {
1065
1048
  }
1066
1049
  /** Track task completion metrics. */
1067
1050
  trackMetrics(status, durationMs, agentId) {
1068
- const m = agentId ? this.getMetricsForAgent(agentId) : this.metrics;
1051
+ const m = this.getMetricsForAgent(agentId);
1069
1052
  m.taskCount++;
1070
1053
  if (status === 'done')
1071
1054
  m.successCount++;
@@ -885,8 +885,8 @@ ${skillContent.slice(0, 15000)}`;
885
885
  // Track metrics + reflection on the adapter
886
886
  if (adapter.type === 'claude-code') {
887
887
  const ccAdapter = adapter;
888
- ccAdapter.trackMetricsPublic('done', totalDurationMs, savedState.agentId);
889
- ccAdapter.runReflection({ id: taskId, description: firstMessage, repoPath: cwd, enableReflection: true }, lastResumeOutput || 'Task completed (no follow-up)', 'done', cwd).then(() => ccAdapter.runSelfImprovement(cwd, savedState.agentId)).catch(() => { });
888
+ ccAdapter.trackMetricsPublic('done', totalDurationMs, agentId);
889
+ ccAdapter.runReflection({ id: taskId, description: firstMessage, repoPath: cwd, enableReflection: true }, lastResumeOutput || 'Task completed (no follow-up)', 'done', cwd).then(() => ccAdapter.runSelfImprovement(cwd, agentId)).catch(() => { });
890
890
  }
891
891
  return;
892
892
  }
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
  import os from 'os';
3
3
  import { spawn } from 'child_process';
4
+ import { StringDecoder } from 'string_decoder';
4
5
  import { runClaude } from '../utils/claude-cli.js';
5
6
  import { validatePath } from '../utils/validate-path.js';
6
7
  const NEEDS_INPUT_MARKER = '"status":"NEEDS_INPUT"';
@@ -82,8 +83,9 @@ export async function runTask(task, onProgress, signal, confirmBeforeEdit) {
82
83
  let stdout = '';
83
84
  let stderr = '';
84
85
  let buffer = '';
86
+ const stdoutDecoder = new StringDecoder('utf8');
85
87
  proc.stdout.on('data', (chunk) => {
86
- const text = chunk.toString();
88
+ const text = stdoutDecoder.write(chunk);
87
89
  stdout += text;
88
90
  buffer += text;
89
91
  const lines = buffer.split('\n');
@@ -100,8 +102,9 @@ export async function runTask(task, onProgress, signal, confirmBeforeEdit) {
100
102
  }
101
103
  }
102
104
  });
105
+ const stderrDecoder = new StringDecoder('utf8');
103
106
  proc.stderr.on('data', (chunk) => {
104
- stderr += chunk.toString();
107
+ stderr += stderrDecoder.write(chunk);
105
108
  });
106
109
  proc.on('close', (code) => {
107
110
  clearTimeout(timeoutTimer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"