tuna-agent 0.1.7 → 0.1.9

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
- // Agent team mode: direct chat with Claude CLI (no PM layer)
51
- if (task.mode === 'agent_team') {
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() {
@@ -185,7 +185,7 @@ export async function handleGenerateScript(ws, code, taskId, idea, topic, style,
185
185
  const resolvedStyle = style || 'short-form';
186
186
  const resolvedDuration = duration || 60;
187
187
  const resolvedLanguage = language || 'vi';
188
- console.log(`[generate_script] Received: style=${style} duration=${duration} (resolved=${resolvedDuration}) language=${language}`);
188
+ console.log(`[generate_script] Received: style=${style} duration=${duration} (resolved=${resolvedDuration}) language=${language} styleName=${styleName || '(none)'} styleGuidance=${styleGuidance ? styleGuidance.length + ' chars' : '(none)'}`);
189
189
  const expandedTemplate = template
190
190
  .replace(/\$ARGUMENTS/g, idea)
191
191
  .replace(/\$IDEA/g, idea)
@@ -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 'tuna' for backward compat */
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;
@@ -43,6 +43,7 @@ export interface TaskAssignment {
43
43
  attachments?: ChatAttachment[];
44
44
  source?: 'manual' | 'skill' | 'scheduled' | 'workflow' | 'api';
45
45
  agentId?: string;
46
+ enableReflection?: boolean;
46
47
  }
47
48
  export interface ConnectResponse {
48
49
  machine_id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"