tuna-agent 0.1.39 → 0.1.42

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.
@@ -24,9 +24,12 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
24
24
  private readonly agentConfig;
25
25
  private taskCount;
26
26
  private static readonly PATTERN_CHECK_INTERVAL;
27
- private metrics;
27
+ private metricsMap;
28
+ private currentAgentId;
29
+ /** Returns metrics for the currently active agent, initializing if needed. */
30
+ private get metrics();
28
31
  constructor(config: AgentConfig);
29
- /** Get current agent metrics snapshot. */
32
+ /** Get all per-agent metrics for heartbeat. */
30
33
  getMetrics(): Record<string, unknown>;
31
34
  checkHealth(): Promise<{
32
35
  ok: boolean;
@@ -14,30 +14,42 @@ export class ClaudeCodeAdapter {
14
14
  agentConfig;
15
15
  taskCount = 0;
16
16
  static PATTERN_CHECK_INTERVAL = 5; // Check patterns every N tasks
17
- // Agent performance metrics
18
- metrics = {
19
- taskCount: 0,
20
- successCount: 0,
21
- failCount: 0,
22
- totalDurationMs: 0,
23
- avgDurationMs: 0,
24
- reflectionCount: 0,
25
- reflectionSkipCount: 0,
26
- memoryCount: 0,
27
- patternsLearnedCount: 0,
28
- rulesCount: 0,
29
- lastTaskAt: null,
30
- lastReflectionAt: null,
31
- lastPatternAt: null,
32
- latestLearnedRule: null,
33
- upSince: new Date().toISOString(),
34
- };
17
+ // Per-agent metrics keyed by agentId (one daemon can handle multiple agents)
18
+ metricsMap = new Map();
19
+ currentAgentId = '';
20
+ /** Returns metrics for the currently active agent, initializing if needed. */
21
+ get metrics() {
22
+ if (!this.metricsMap.has(this.currentAgentId)) {
23
+ this.metricsMap.set(this.currentAgentId, {
24
+ taskCount: 0,
25
+ successCount: 0,
26
+ failCount: 0,
27
+ totalDurationMs: 0,
28
+ avgDurationMs: 0,
29
+ reflectionCount: 0,
30
+ reflectionSkipCount: 0,
31
+ memoryCount: 0,
32
+ patternsLearnedCount: 0,
33
+ rulesCount: 0,
34
+ lastTaskAt: null,
35
+ lastReflectionAt: null,
36
+ lastPatternAt: null,
37
+ latestLearnedRule: null,
38
+ upSince: new Date().toISOString(),
39
+ });
40
+ }
41
+ return this.metricsMap.get(this.currentAgentId);
42
+ }
35
43
  constructor(config) {
36
44
  this.agentConfig = config;
37
45
  }
38
- /** Get current agent metrics snapshot. */
46
+ /** Get all per-agent metrics for heartbeat. */
39
47
  getMetrics() {
40
- return { ...this.metrics };
48
+ const agentMetricsMap = {};
49
+ this.metricsMap.forEach((m, agentId) => {
50
+ agentMetricsMap[agentId] = { ...m };
51
+ });
52
+ return { agentMetricsMap };
41
53
  }
42
54
  async checkHealth() {
43
55
  try {
@@ -73,6 +85,8 @@ export class ClaudeCodeAdapter {
73
85
  : undefined;
74
86
  // Default mode: direct chat with Claude CLI (no PM layer)
75
87
  // Only use PM planning when mode is explicitly 'tuna'
88
+ // Set current agent context for per-agent metrics tracking
89
+ this.currentAgentId = task.agentId || '';
76
90
  if (task.mode !== 'tuna') {
77
91
  console.log(`[ClaudeCode] Agent Team mode — direct chat with Claude CLI`);
78
92
  ws.sendProgress(task.id, 'executing', { startedAt: new Date().toISOString() });
@@ -113,7 +113,9 @@ export async function handleGenerateIdeas(ws, code, taskId, topic, styleName, st
113
113
  let resultText;
114
114
  if (provider === 'openai') {
115
115
  console.log(`[generate_ideas] Using OpenAI provider`);
116
+ console.log(`[generate_ideas] Calling OpenAI API...`);
116
117
  const oaiResult = await runOpenAI({ prompt, systemPrompt, timeoutMs: 60000 });
118
+ console.log(`[generate_ideas] OpenAI response received: ${oaiResult.result.length} chars, model=${oaiResult.model}`);
117
119
  resultText = oaiResult.result;
118
120
  }
119
121
  else {
@@ -163,6 +165,7 @@ export async function handleGenerateIdeas(ws, code, taskId, topic, styleName, st
163
165
  }
164
166
  catch (err) {
165
167
  const error = err instanceof Error ? err.message : String(err);
168
+ console.error(`[generate_ideas] Error: ${error}`);
166
169
  ws.sendExtensionEvent(code, { type: 'ideas_result', ideas: [], error });
167
170
  ws.sendExtensionDone(code, taskId, { error });
168
171
  }
@@ -1,6 +1,2 @@
1
1
  import type { AgentConfig } from '../types/index.js';
2
- /**
3
- * Start the agent daemon.
4
- * Connects to API via WebSocket, receives tasks, delegates to agent adapter.
5
- */
6
2
  export declare function startDaemon(config: AgentConfig): Promise<void>;
@@ -17,6 +17,18 @@ import { setupMcpConfig } from '../mcp/setup.js';
17
17
  * Start the agent daemon.
18
18
  * Connects to API via WebSocket, receives tasks, delegates to agent adapter.
19
19
  */
20
+ // Load persisted Mem0 config into process.env before any dynamic imports read it
21
+ (function loadMem0FromConfig() {
22
+ const saved = loadConfig();
23
+ if (!saved)
24
+ return;
25
+ if (saved.mem0SshHost && !process.env.MEM0_SSH_HOST)
26
+ process.env.MEM0_SSH_HOST = saved.mem0SshHost;
27
+ if (saved.mem0SshPort && !process.env.MEM0_SSH_PORT)
28
+ process.env.MEM0_SSH_PORT = saved.mem0SshPort;
29
+ if (saved.mem0HttpBase && !process.env.MEM0_HTTP_BASE)
30
+ process.env.MEM0_HTTP_BASE = saved.mem0HttpBase;
31
+ })();
20
32
  export async function startDaemon(config) {
21
33
  // Create agent adapter based on config
22
34
  const agentConfig = {
@@ -67,6 +79,33 @@ export async function startDaemon(config) {
67
79
  switch (type) {
68
80
  case 'connected':
69
81
  console.log(`[Daemon] Connected as "${msg.name}" (${msg.agentId})`);
82
+ // Persist Mem0 config pushed from server so it survives daemon restarts
83
+ if (msg.mem0Config && typeof msg.mem0Config === 'object') {
84
+ const m = msg.mem0Config;
85
+ const current = loadConfig();
86
+ if (current) {
87
+ let changed = false;
88
+ if (m.sshHost) {
89
+ current.mem0SshHost = m.sshHost;
90
+ process.env.MEM0_SSH_HOST = m.sshHost;
91
+ changed = true;
92
+ }
93
+ if (m.sshPort) {
94
+ current.mem0SshPort = m.sshPort;
95
+ process.env.MEM0_SSH_PORT = m.sshPort;
96
+ changed = true;
97
+ }
98
+ if (m.httpBase) {
99
+ current.mem0HttpBase = m.httpBase;
100
+ process.env.MEM0_HTTP_BASE = m.httpBase;
101
+ changed = true;
102
+ }
103
+ if (changed) {
104
+ saveConfig(current);
105
+ console.log('[Daemon] Mem0 config saved from server');
106
+ }
107
+ }
108
+ }
70
109
  // Recover orphaned tasks — pass activeTaskId so API won't fail a task we're still running
71
110
  ws.send({ action: 'recover_orphaned_tasks', activeTaskId: currentTaskId ?? undefined });
72
111
  break;
@@ -11,6 +11,9 @@ export interface AgentConfig {
11
11
  openclawGatewayUrl?: string;
12
12
  };
13
13
  extensionCodes?: string[];
14
+ mem0SshHost?: string;
15
+ mem0SshPort?: string;
16
+ mem0HttpBase?: string;
14
17
  }
15
18
  export interface AgentCapabilities {
16
19
  supports_teams: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.39",
3
+ "version": "0.1.42",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"