tuna-agent 0.1.28 → 0.1.29
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.
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import type { AgentAdapter, AgentType } from './types.js';
|
|
2
2
|
import type { AgentConfig, TaskAssignment, InputResponse } from '../types/index.js';
|
|
3
3
|
import type { AgentWebSocketClient } from '../daemon/ws-client.js';
|
|
4
|
+
export interface AgentMetrics {
|
|
5
|
+
taskCount: number;
|
|
6
|
+
successCount: number;
|
|
7
|
+
failCount: number;
|
|
8
|
+
totalDurationMs: number;
|
|
9
|
+
avgDurationMs: number;
|
|
10
|
+
reflectionCount: number;
|
|
11
|
+
reflectionSkipCount: number;
|
|
12
|
+
patternsLearnedCount: number;
|
|
13
|
+
lastTaskAt: string | null;
|
|
14
|
+
upSince: string;
|
|
15
|
+
}
|
|
4
16
|
export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
5
17
|
readonly type: AgentType;
|
|
6
18
|
readonly displayName = "Claude Code";
|
|
7
19
|
private readonly agentConfig;
|
|
8
20
|
private taskCount;
|
|
9
21
|
private static readonly PATTERN_CHECK_INTERVAL;
|
|
22
|
+
private metrics;
|
|
10
23
|
constructor(config: AgentConfig);
|
|
24
|
+
/** Get current agent metrics snapshot. */
|
|
25
|
+
getMetrics(): Record<string, unknown>;
|
|
11
26
|
checkHealth(): Promise<{
|
|
12
27
|
ok: boolean;
|
|
13
28
|
message: string;
|
|
@@ -33,5 +48,7 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
33
48
|
* Runs every N tasks to evolve the agent's permanent knowledge.
|
|
34
49
|
*/
|
|
35
50
|
runSelfImprovement(cwd: string): Promise<void>;
|
|
51
|
+
/** Track task completion metrics. */
|
|
52
|
+
private trackMetrics;
|
|
36
53
|
dispose(): Promise<void>;
|
|
37
54
|
}
|
|
@@ -14,9 +14,26 @@ 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
|
+
patternsLearnedCount: 0,
|
|
27
|
+
lastTaskAt: null,
|
|
28
|
+
upSince: new Date().toISOString(),
|
|
29
|
+
};
|
|
17
30
|
constructor(config) {
|
|
18
31
|
this.agentConfig = config;
|
|
19
32
|
}
|
|
33
|
+
/** Get current agent metrics snapshot. */
|
|
34
|
+
getMetrics() {
|
|
35
|
+
return { ...this.metrics };
|
|
36
|
+
}
|
|
20
37
|
async checkHealth() {
|
|
21
38
|
try {
|
|
22
39
|
execSync('which claude', { stdio: 'ignore' });
|
|
@@ -202,6 +219,7 @@ export class ClaudeCodeAdapter {
|
|
|
202
219
|
startedAt: firstChunkIso || undefined,
|
|
203
220
|
});
|
|
204
221
|
ws.sendTaskFailed(task.id, result.result);
|
|
222
|
+
this.trackMetrics('failed', totalDurationMs);
|
|
205
223
|
console.log(`[ClaudeCode] Agent Team task ${task.id} failed in round ${round + 1}`);
|
|
206
224
|
this.runReflection(task, result.result, 'failed', cwd).catch(() => { });
|
|
207
225
|
return;
|
|
@@ -289,6 +307,7 @@ export class ClaudeCodeAdapter {
|
|
|
289
307
|
});
|
|
290
308
|
await new Promise(resolve => setTimeout(resolve, 150));
|
|
291
309
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
310
|
+
this.trackMetrics('done', totalDurationMs);
|
|
292
311
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
293
312
|
// Post-task reflection with actual output (non-blocking)
|
|
294
313
|
this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
|
|
@@ -690,12 +709,14 @@ export class ClaudeCodeAdapter {
|
|
|
690
709
|
const { callMem0Reflect, callMem0AddMemory } = await import('../mcp/setup.js');
|
|
691
710
|
const aiReflection = await callMem0Reflect(task.description, resultSummary, status);
|
|
692
711
|
if (!aiReflection) {
|
|
712
|
+
this.metrics.reflectionSkipCount++;
|
|
693
713
|
console.log(`[Reflection] No meaningful lesson — skipping storage for task ${task.id}`);
|
|
694
714
|
return;
|
|
695
715
|
}
|
|
696
716
|
// Step 2: Store the AI-generated reflection in Mem0
|
|
697
717
|
console.log(`[Reflection] Storing: "${aiReflection.substring(0, 100)}..."`);
|
|
698
718
|
await callMem0AddMemory(aiReflection, this.agentConfig.name);
|
|
719
|
+
this.metrics.reflectionCount++;
|
|
699
720
|
console.log(`[Reflection] Stored for task ${task.id}`);
|
|
700
721
|
}
|
|
701
722
|
catch (err) {
|
|
@@ -783,6 +804,7 @@ export class ClaudeCodeAdapter {
|
|
|
783
804
|
const separator = existingContent.endsWith('\n') ? '\n' : '\n\n';
|
|
784
805
|
fs.writeFileSync(claudeMdPath, existingContent + separator + `${SECTION_HEADER}\n${rulesBlock}\n`);
|
|
785
806
|
}
|
|
807
|
+
this.metrics.patternsLearnedCount += newPatterns.length;
|
|
786
808
|
console.log(`[Self-Improve] Added ${newPatterns.length} new rules to CLAUDE.md:`);
|
|
787
809
|
newPatterns.forEach(p => console.log(`[Self-Improve] - ${p.rule}`));
|
|
788
810
|
}
|
|
@@ -790,6 +812,18 @@ export class ClaudeCodeAdapter {
|
|
|
790
812
|
console.warn(`[Self-Improve] Failed:`, err instanceof Error ? err.message : err);
|
|
791
813
|
}
|
|
792
814
|
}
|
|
815
|
+
/** Track task completion metrics. */
|
|
816
|
+
trackMetrics(status, durationMs) {
|
|
817
|
+
this.metrics.taskCount++;
|
|
818
|
+
if (status === 'done')
|
|
819
|
+
this.metrics.successCount++;
|
|
820
|
+
else
|
|
821
|
+
this.metrics.failCount++;
|
|
822
|
+
this.metrics.totalDurationMs += durationMs;
|
|
823
|
+
this.metrics.avgDurationMs = Math.round(this.metrics.totalDurationMs / this.metrics.taskCount);
|
|
824
|
+
this.metrics.lastTaskAt = new Date().toISOString();
|
|
825
|
+
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}`);
|
|
826
|
+
}
|
|
793
827
|
async dispose() {
|
|
794
828
|
// No persistent resources to clean up
|
|
795
829
|
}
|
package/dist/agents/types.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ export interface AgentAdapter {
|
|
|
24
24
|
* plan → execute → report progress/results via ws.
|
|
25
25
|
*/
|
|
26
26
|
handleTask(task: TaskAssignment, ws: AgentWebSocketClient, pendingInputResolvers: Map<string, (response: InputResponse) => void>, signal?: AbortSignal, pendingPermissionResolvers?: Map<string, (approved: boolean) => void>): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get current agent performance metrics.
|
|
29
|
+
*/
|
|
30
|
+
getMetrics?(): Record<string, unknown>;
|
|
27
31
|
/**
|
|
28
32
|
* Clean up resources on shutdown.
|
|
29
33
|
*/
|
package/dist/daemon/index.js
CHANGED
|
@@ -750,6 +750,10 @@ ${skillContent.slice(0, 15000)}`;
|
|
|
750
750
|
wsClient.send({ action: 'agent_ready' });
|
|
751
751
|
}
|
|
752
752
|
}
|
|
753
|
+
// Wire up agent metrics to heartbeat
|
|
754
|
+
if (adapter.getMetrics) {
|
|
755
|
+
ws.setMetricsProvider(() => adapter.getMetrics());
|
|
756
|
+
}
|
|
753
757
|
ws.connect();
|
|
754
758
|
const shutdown = () => {
|
|
755
759
|
console.log('\n[Daemon] Shutting down...');
|
|
@@ -10,11 +10,14 @@ export declare class AgentWebSocketClient {
|
|
|
10
10
|
private running;
|
|
11
11
|
private onMessage;
|
|
12
12
|
private onAuthFailed?;
|
|
13
|
+
private getAgentMetrics?;
|
|
13
14
|
/** Pending flow command callbacks keyed by request ID */
|
|
14
15
|
private _flowCallbacks;
|
|
15
16
|
constructor(config: AgentConfig, onMessage: MessageHandler, onAuthFailed?: (code: number, reason: string) => void);
|
|
16
17
|
connect(): void;
|
|
17
18
|
disconnect(): void;
|
|
19
|
+
/** Register a callback to get agent metrics for heartbeat. */
|
|
20
|
+
setMetricsProvider(fn: () => Record<string, unknown>): void;
|
|
18
21
|
/**
|
|
19
22
|
* Send a message to the API server.
|
|
20
23
|
*/
|
package/dist/daemon/ws-client.js
CHANGED
|
@@ -15,6 +15,7 @@ export class AgentWebSocketClient {
|
|
|
15
15
|
running = false;
|
|
16
16
|
onMessage;
|
|
17
17
|
onAuthFailed;
|
|
18
|
+
getAgentMetrics;
|
|
18
19
|
/** Pending flow command callbacks keyed by request ID */
|
|
19
20
|
_flowCallbacks = new Map();
|
|
20
21
|
constructor(config, onMessage, onAuthFailed) {
|
|
@@ -41,6 +42,10 @@ export class AgentWebSocketClient {
|
|
|
41
42
|
this.ws = null;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
45
|
+
/** Register a callback to get agent metrics for heartbeat. */
|
|
46
|
+
setMetricsProvider(fn) {
|
|
47
|
+
this.getAgentMetrics = fn;
|
|
48
|
+
}
|
|
44
49
|
/**
|
|
45
50
|
* Send a message to the API server.
|
|
46
51
|
*/
|
|
@@ -273,11 +278,15 @@ export class AgentWebSocketClient {
|
|
|
273
278
|
this._stopHeartbeat();
|
|
274
279
|
this.heartbeatTimer = setInterval(() => {
|
|
275
280
|
const agentType = this.config.agentType || 'claude-code';
|
|
276
|
-
|
|
281
|
+
const heartbeat = {
|
|
277
282
|
action: 'heartbeat',
|
|
278
283
|
system_load: getSystemLoad(),
|
|
279
284
|
capabilities: detectCapabilities(agentType),
|
|
280
|
-
}
|
|
285
|
+
};
|
|
286
|
+
if (this.getAgentMetrics) {
|
|
287
|
+
heartbeat.agent_metrics = this.getAgentMetrics();
|
|
288
|
+
}
|
|
289
|
+
this.send(heartbeat);
|
|
281
290
|
}, 30000);
|
|
282
291
|
}
|
|
283
292
|
_stopHeartbeat() {
|