tuna-agent 0.1.8 → 0.1.10
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.
|
@@ -11,5 +11,20 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
11
11
|
message: string;
|
|
12
12
|
}>;
|
|
13
13
|
handleTask(task: TaskAssignment, ws: AgentWebSocketClient, pendingInputResolvers: Map<string, (response: InputResponse) => void>, signal?: AbortSignal, pendingPermissionResolvers?: Map<string, (approved: boolean) => void>): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Run post-task self-reflection via Mem0.
|
|
16
|
+
* Agent reviews what it did and stores lessons learned.
|
|
17
|
+
*/
|
|
18
|
+
runReflection(task: TaskAssignment, resultSummary: string, status: 'done' | 'failed', cwd: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Store user rating feedback into Mem0.
|
|
21
|
+
*/
|
|
22
|
+
storeRatingMemory(data: {
|
|
23
|
+
taskTitle: string;
|
|
24
|
+
taskDescription: string;
|
|
25
|
+
score: number;
|
|
26
|
+
comment?: string;
|
|
27
|
+
cwd: string;
|
|
28
|
+
}): Promise<void>;
|
|
14
29
|
dispose(): Promise<void>;
|
|
15
30
|
}
|
|
@@ -47,8 +47,9 @@ export class ClaudeCodeAdapter {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
: undefined;
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// Default mode: direct chat with Claude CLI (no PM layer)
|
|
51
|
+
// Only use PM planning when mode is explicitly 'tuna'
|
|
52
|
+
if (task.mode !== 'tuna') {
|
|
52
53
|
console.log(`[ClaudeCode] Agent Team mode — direct chat with Claude CLI`);
|
|
53
54
|
ws.sendProgress(task.id, 'executing', { startedAt: new Date().toISOString() });
|
|
54
55
|
ws.sendSubtaskStart(task.id, { id: 'agent-team', role: 'agent', description: task.description });
|
|
@@ -184,6 +185,7 @@ export class ClaudeCodeAdapter {
|
|
|
184
185
|
});
|
|
185
186
|
ws.sendTaskFailed(task.id, result.result);
|
|
186
187
|
console.log(`[ClaudeCode] Agent Team task ${task.id} failed in round ${round + 1}`);
|
|
188
|
+
this.runReflection(task, result.result, 'failed', cwd).catch(() => { });
|
|
187
189
|
return;
|
|
188
190
|
}
|
|
189
191
|
// Send finalized message for the last turn's remaining text
|
|
@@ -261,6 +263,8 @@ export class ClaudeCodeAdapter {
|
|
|
261
263
|
await new Promise(resolve => setTimeout(resolve, 150));
|
|
262
264
|
ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
|
|
263
265
|
console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
|
|
266
|
+
// Post-task reflection (non-blocking — runs after task is marked done)
|
|
267
|
+
this.runReflection(task, 'Agent Team task completed', 'done', task.repoPath).catch(() => { });
|
|
264
268
|
}
|
|
265
269
|
finally {
|
|
266
270
|
cleanupAttachments(task.id);
|
|
@@ -635,6 +639,78 @@ export class ClaudeCodeAdapter {
|
|
|
635
639
|
content: `Task completed.`,
|
|
636
640
|
});
|
|
637
641
|
console.log(`[ClaudeCode] Task ${task.id} completed`);
|
|
642
|
+
// Post-task reflection
|
|
643
|
+
this.runReflection(task, plan.summary, 'done', task.repoPath).catch(() => { });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Run post-task self-reflection via Mem0.
|
|
648
|
+
* Agent reviews what it did and stores lessons learned.
|
|
649
|
+
*/
|
|
650
|
+
async runReflection(task, resultSummary, status, cwd) {
|
|
651
|
+
if (task.enableReflection === false)
|
|
652
|
+
return;
|
|
653
|
+
if (!process.env.MEM0_SSH_HOST)
|
|
654
|
+
return; // Mem0 not configured
|
|
655
|
+
// Skip trivial tasks (< 30s would have no meaningful reflection)
|
|
656
|
+
const MIN_DESCRIPTION_LENGTH = 20;
|
|
657
|
+
if (task.description.length < MIN_DESCRIPTION_LENGTH)
|
|
658
|
+
return;
|
|
659
|
+
const truncatedDesc = task.description.substring(0, 300);
|
|
660
|
+
const truncatedResult = resultSummary.substring(0, 500);
|
|
661
|
+
const reflectionPrompt = `You just completed a task. Briefly reflect on what happened and store your reflection using the mem0 add_memory tool.
|
|
662
|
+
|
|
663
|
+
Task: ${truncatedDesc}
|
|
664
|
+
Status: ${status}
|
|
665
|
+
Result summary: ${truncatedResult}
|
|
666
|
+
|
|
667
|
+
Instructions:
|
|
668
|
+
1. Think about what went well and what could be improved (2-3 sentences max)
|
|
669
|
+
2. Call the mem0 add_memory tool to store your reflection
|
|
670
|
+
3. Do NOT do anything else — no file edits, no other tool calls`;
|
|
671
|
+
try {
|
|
672
|
+
console.log(`[Reflection] Starting reflection for task ${task.id} (${status})`);
|
|
673
|
+
await runClaude({
|
|
674
|
+
prompt: reflectionPrompt,
|
|
675
|
+
cwd,
|
|
676
|
+
maxTurns: 3,
|
|
677
|
+
outputFormat: 'json',
|
|
678
|
+
timeoutMs: 30000,
|
|
679
|
+
permissionMode: 'bypassPermissions',
|
|
680
|
+
});
|
|
681
|
+
console.log(`[Reflection] Reflection complete for task ${task.id}`);
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
// Non-fatal — don't let reflection failure affect task flow
|
|
685
|
+
console.warn(`[Reflection] Failed for task ${task.id}:`, err instanceof Error ? err.message : err);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Store user rating feedback into Mem0.
|
|
690
|
+
*/
|
|
691
|
+
async storeRatingMemory(data) {
|
|
692
|
+
if (!process.env.MEM0_SSH_HOST)
|
|
693
|
+
return;
|
|
694
|
+
const sentiment = data.score > 0 ? 'approved (thumbs up)' : 'rejected (thumbs down)';
|
|
695
|
+
const commentPart = data.comment ? ` User feedback: "${data.comment}"` : '';
|
|
696
|
+
const memoryText = `User ${sentiment} task "${data.taskTitle}".${commentPart} Task description: ${data.taskDescription.substring(0, 200)}`;
|
|
697
|
+
const prompt = `Store this user feedback in your memory using the mem0 add_memory tool. Do NOT do anything else.
|
|
698
|
+
|
|
699
|
+
Memory to store: ${memoryText}`;
|
|
700
|
+
try {
|
|
701
|
+
console.log(`[Rating→Mem0] Storing rating for task "${data.taskTitle}" (${data.score > 0 ? '👍' : '👎'})`);
|
|
702
|
+
await runClaude({
|
|
703
|
+
prompt,
|
|
704
|
+
cwd: data.cwd,
|
|
705
|
+
maxTurns: 2,
|
|
706
|
+
outputFormat: 'json',
|
|
707
|
+
timeoutMs: 20000,
|
|
708
|
+
permissionMode: 'bypassPermissions',
|
|
709
|
+
});
|
|
710
|
+
console.log(`[Rating→Mem0] Rating stored successfully`);
|
|
711
|
+
}
|
|
712
|
+
catch (err) {
|
|
713
|
+
console.warn(`[Rating→Mem0] Failed:`, err instanceof Error ? err.message : err);
|
|
638
714
|
}
|
|
639
715
|
}
|
|
640
716
|
async dispose() {
|
package/dist/daemon/index.js
CHANGED
|
@@ -276,6 +276,28 @@ ${skillContent.slice(0, 15000)}`;
|
|
|
276
276
|
}
|
|
277
277
|
})();
|
|
278
278
|
}
|
|
279
|
+
else if (command === 'task_rated') {
|
|
280
|
+
const score = msg.score;
|
|
281
|
+
const comment = msg.comment;
|
|
282
|
+
const taskTitle = msg.taskTitle || '';
|
|
283
|
+
const taskDescription = msg.taskDescription || '';
|
|
284
|
+
const agentId = msg.agentId || '';
|
|
285
|
+
console.log(`[Daemon] Task rated: ${score > 0 ? '👍' : '👎'} "${taskTitle}"`);
|
|
286
|
+
// Store rating in Mem0 via adapter (fire-and-forget)
|
|
287
|
+
if (adapter.type === 'claude-code') {
|
|
288
|
+
const ccAdapter = adapter;
|
|
289
|
+
const defaultCwd = path.join(os.homedir(), 'tuna-workspace');
|
|
290
|
+
ccAdapter.storeRatingMemory({
|
|
291
|
+
taskTitle,
|
|
292
|
+
taskDescription,
|
|
293
|
+
score,
|
|
294
|
+
comment: comment || undefined,
|
|
295
|
+
cwd: defaultCwd,
|
|
296
|
+
}).catch((err) => {
|
|
297
|
+
console.warn(`[Daemon] Rating→Mem0 failed:`, err instanceof Error ? err.message : err);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
279
301
|
else {
|
|
280
302
|
console.log(`[Daemon] Unknown command: ${command}`);
|
|
281
303
|
}
|
|
@@ -3,7 +3,7 @@ export interface PMSessionState {
|
|
|
3
3
|
pmSessionId: string;
|
|
4
4
|
repoPath: string;
|
|
5
5
|
savedAt: string;
|
|
6
|
-
/** Task mode — defaults to '
|
|
6
|
+
/** Task mode — defaults to 'agent_team' (direct Claude CLI) */
|
|
7
7
|
mode?: 'tuna' | 'agent_team';
|
|
8
8
|
/** Claude CLI session ID for agent_team resume */
|
|
9
9
|
agentTeamSessionId?: string;
|
package/dist/mcp/setup.js
CHANGED
|
@@ -37,7 +37,7 @@ function buildMem0McpConfig(agentName) {
|
|
|
37
37
|
env: envWithUser,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
const envString = Object.entries(envWithUser).map(([k, v]) => `${k}
|
|
40
|
+
const envString = Object.entries(envWithUser).map(([k, v]) => `${k}='${String(v).replace(/'/g, "'\\''")}'`).join(' ');
|
|
41
41
|
const args = ['-p', MEM0_SSH_PORT, '-o', 'StrictHostKeyChecking=no'];
|
|
42
42
|
if (MEM0_SSH_KEY) {
|
|
43
43
|
args.push('-i', MEM0_SSH_KEY);
|
package/dist/types/index.d.ts
CHANGED