tuna-agent 0.1.40 → 0.1.43
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
|
|
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
|
|
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
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
46
|
+
/** Get all per-agent metrics for heartbeat. */
|
|
39
47
|
getMetrics() {
|
|
40
|
-
|
|
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() });
|
|
@@ -93,10 +107,12 @@ export class ClaudeCodeAdapter {
|
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
109
|
// Pre-task Memory Recall: search Mem0 for relevant past learnings
|
|
110
|
+
const defaultWorkspaceForRecall = path.join(os.homedir(), 'tuna-workspace');
|
|
111
|
+
const recallAgentName = path.basename(task.repoPath || defaultWorkspaceForRecall);
|
|
96
112
|
if (process.env.MEM0_SSH_HOST && task.description.length >= 20) {
|
|
97
113
|
try {
|
|
98
114
|
const { callMem0SearchMemory } = await import('../mcp/setup.js');
|
|
99
|
-
const memories = await callMem0SearchMemory(task.description,
|
|
115
|
+
const memories = await callMem0SearchMemory(task.description, recallAgentName, 5);
|
|
100
116
|
if (memories.length > 0) {
|
|
101
117
|
const memoryContext = memories.map(m => `- ${m}`).join('\n');
|
|
102
118
|
userMessage = `${task.description}\n\n<past_learnings>\nRelevant lessons from previous tasks:\n${memoryContext}\n</past_learnings>`;
|
|
@@ -733,8 +749,9 @@ export class ClaudeCodeAdapter {
|
|
|
733
749
|
return;
|
|
734
750
|
}
|
|
735
751
|
// Step 2: Store the AI-generated reflection in Mem0
|
|
752
|
+
const agentFolderNameForReflection = path.basename(cwd);
|
|
736
753
|
console.log(`[Reflection] Storing: "${aiReflection.substring(0, 100)}..."`);
|
|
737
|
-
await callMem0AddMemory(aiReflection,
|
|
754
|
+
await callMem0AddMemory(aiReflection, agentFolderNameForReflection);
|
|
738
755
|
this.metrics.reflectionCount++;
|
|
739
756
|
this.metrics.memoryCount++;
|
|
740
757
|
this.metrics.lastReflectionAt = new Date().toISOString();
|
|
@@ -748,7 +765,7 @@ export class ClaudeCodeAdapter {
|
|
|
748
765
|
? `Task failed: "${task.description.substring(0, 150)}". Error: ${resultSummary.substring(0, 200)}`
|
|
749
766
|
: `Task completed: "${task.description.substring(0, 150)}". Result: ${resultSummary.substring(0, 200)}`;
|
|
750
767
|
const { callMem0AddMemory } = await import('../mcp/setup.js');
|
|
751
|
-
await callMem0AddMemory(fallback,
|
|
768
|
+
await callMem0AddMemory(fallback, path.basename(cwd));
|
|
752
769
|
}
|
|
753
770
|
catch {
|
|
754
771
|
// Both AI and fallback failed — give up silently
|
|
@@ -771,7 +788,7 @@ export class ClaudeCodeAdapter {
|
|
|
771
788
|
try {
|
|
772
789
|
console.log(`[Rating→Mem0] Storing rating for task "${data.taskTitle}" (${data.score > 0 ? '👍' : '👎'})`);
|
|
773
790
|
const { callMem0AddMemory } = await import('../mcp/setup.js');
|
|
774
|
-
await callMem0AddMemory(memoryText,
|
|
791
|
+
await callMem0AddMemory(memoryText, path.basename(data.cwd));
|
|
775
792
|
console.log(`[Rating→Mem0] Rating stored successfully`);
|
|
776
793
|
}
|
|
777
794
|
catch (err) {
|
|
@@ -791,7 +808,7 @@ export class ClaudeCodeAdapter {
|
|
|
791
808
|
try {
|
|
792
809
|
console.log(`[Self-Improve] Running pattern detection (every ${ClaudeCodeAdapter.PATTERN_CHECK_INTERVAL} tasks, count=${this.taskCount})`);
|
|
793
810
|
const { callMem0Patterns } = await import('../mcp/setup.js');
|
|
794
|
-
const patterns = await callMem0Patterns(
|
|
811
|
+
const patterns = await callMem0Patterns(path.basename(cwd), 3);
|
|
795
812
|
if (patterns.length === 0) {
|
|
796
813
|
console.log(`[Self-Improve] No patterns detected yet`);
|
|
797
814
|
return;
|
package/dist/daemon/index.d.ts
CHANGED
package/dist/daemon/index.js
CHANGED
|
@@ -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;
|
package/dist/types/index.d.ts
CHANGED