tuna-agent 0.1.28 → 0.1.30
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;
|
|
@@ -256,6 +274,12 @@ export class ClaudeCodeAdapter {
|
|
|
256
274
|
if (err instanceof Error && err.message === '__FOLLOW_UP_TIMEOUT__') {
|
|
257
275
|
console.log(`[ClaudeCode] No follow-up after ${FOLLOW_UP_TIMEOUT_MS / 1000}s — closing task`);
|
|
258
276
|
pendingInputResolvers.delete(task.id);
|
|
277
|
+
ws.sendTaskDone(task.id, {
|
|
278
|
+
result: 'Agent Team task completed',
|
|
279
|
+
durationMs: totalDurationMs,
|
|
280
|
+
sessionId,
|
|
281
|
+
});
|
|
282
|
+
this.trackMetrics('done', totalDurationMs);
|
|
259
283
|
const timeoutOutput = lastTaskOutput || 'Task completed (no follow-up)';
|
|
260
284
|
this.runReflection(task, timeoutOutput, 'done', task.repoPath)
|
|
261
285
|
.then(() => this.runSelfImprovement(task.repoPath))
|
|
@@ -289,6 +313,7 @@ export class ClaudeCodeAdapter {
|
|
|
289
313
|
});
|
|
290
314
|
await new Promise(resolve => setTimeout(resolve, 150));
|
|
291
315
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
316
|
+
this.trackMetrics('done', totalDurationMs);
|
|
292
317
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
293
318
|
// Post-task reflection with actual output (non-blocking)
|
|
294
319
|
this.runReflection(task, lastTaskOutput || 'Task completed without text output', 'done', task.repoPath)
|
|
@@ -690,12 +715,14 @@ export class ClaudeCodeAdapter {
|
|
|
690
715
|
const { callMem0Reflect, callMem0AddMemory } = await import('../mcp/setup.js');
|
|
691
716
|
const aiReflection = await callMem0Reflect(task.description, resultSummary, status);
|
|
692
717
|
if (!aiReflection) {
|
|
718
|
+
this.metrics.reflectionSkipCount++;
|
|
693
719
|
console.log(`[Reflection] No meaningful lesson — skipping storage for task ${task.id}`);
|
|
694
720
|
return;
|
|
695
721
|
}
|
|
696
722
|
// Step 2: Store the AI-generated reflection in Mem0
|
|
697
723
|
console.log(`[Reflection] Storing: "${aiReflection.substring(0, 100)}..."`);
|
|
698
724
|
await callMem0AddMemory(aiReflection, this.agentConfig.name);
|
|
725
|
+
this.metrics.reflectionCount++;
|
|
699
726
|
console.log(`[Reflection] Stored for task ${task.id}`);
|
|
700
727
|
}
|
|
701
728
|
catch (err) {
|
|
@@ -783,6 +810,7 @@ export class ClaudeCodeAdapter {
|
|
|
783
810
|
const separator = existingContent.endsWith('\n') ? '\n' : '\n\n';
|
|
784
811
|
fs.writeFileSync(claudeMdPath, existingContent + separator + `${SECTION_HEADER}\n${rulesBlock}\n`);
|
|
785
812
|
}
|
|
813
|
+
this.metrics.patternsLearnedCount += newPatterns.length;
|
|
786
814
|
console.log(`[Self-Improve] Added ${newPatterns.length} new rules to CLAUDE.md:`);
|
|
787
815
|
newPatterns.forEach(p => console.log(`[Self-Improve] - ${p.rule}`));
|
|
788
816
|
}
|
|
@@ -790,6 +818,18 @@ export class ClaudeCodeAdapter {
|
|
|
790
818
|
console.warn(`[Self-Improve] Failed:`, err instanceof Error ? err.message : err);
|
|
791
819
|
}
|
|
792
820
|
}
|
|
821
|
+
/** Track task completion metrics. */
|
|
822
|
+
trackMetrics(status, durationMs) {
|
|
823
|
+
this.metrics.taskCount++;
|
|
824
|
+
if (status === 'done')
|
|
825
|
+
this.metrics.successCount++;
|
|
826
|
+
else
|
|
827
|
+
this.metrics.failCount++;
|
|
828
|
+
this.metrics.totalDurationMs += durationMs;
|
|
829
|
+
this.metrics.avgDurationMs = Math.round(this.metrics.totalDurationMs / this.metrics.taskCount);
|
|
830
|
+
this.metrics.lastTaskAt = new Date().toISOString();
|
|
831
|
+
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}`);
|
|
832
|
+
}
|
|
793
833
|
async dispose() {
|
|
794
834
|
// No persistent resources to clean up
|
|
795
835
|
}
|
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() {
|