tuna-agent 0.1.179 → 0.1.181

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.
@@ -445,8 +445,6 @@ export class ClaudeCodeAdapter {
445
445
  durationMs: totalDurationMs,
446
446
  sessionId,
447
447
  });
448
- await new Promise(resolve => setTimeout(resolve, 150));
449
- ws.sendPMMessage(task.id, { sender: 'pm', content: 'Task completed.' });
450
448
  this.trackMetrics('done', totalDurationMs, localAgentId);
451
449
  console.log(`[ClaudeCode] Agent Team task ${task.id} completed (${(totalDurationMs / 1000).toFixed(1)}s)`);
452
450
  // Post-task reflection with actual output (non-blocking)
@@ -803,11 +801,6 @@ export class ClaudeCodeAdapter {
803
801
  })),
804
802
  },
805
803
  });
806
- await new Promise(resolve => setTimeout(resolve, 150));
807
- ws.sendPMMessage(task.id, {
808
- sender: 'pm',
809
- content: `Task completed.`,
810
- });
811
804
  console.log(`[ClaudeCode] Task ${task.id} completed`);
812
805
  // Post-task reflection
813
806
  this.runReflection(task, plan.summary, 'done', task.repoPath).catch(() => { });
@@ -794,8 +794,6 @@ ${skillContent.slice(0, 15000)}`;
794
794
  if (result.status === 'done' && !result.followUpMessage) {
795
795
  const totalDuration = result.sessions.reduce((sum, s) => sum + (s.durationMs ?? 0), 0);
796
796
  wsClient.sendTaskDone(taskId, { result: plan.summary, durationMs: totalDuration });
797
- await new Promise(resolve => setTimeout(resolve, 150));
798
- wsClient.sendPMMessage(taskId, { sender: 'pm', content: `Task completed.` });
799
797
  }
800
798
  else if (result.followUpMessage) {
801
799
  userMessage = result.followUpMessage;
@@ -333,7 +333,7 @@ export async function executeSubtask(subtask, repoPath, contracts, callbacks, si
333
333
  const result = await runClaude({
334
334
  prompt: subtask.description,
335
335
  cwd,
336
- allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep'],
336
+ allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep', 'mcp__mem0', 'mcp__tuna-knowledge', 'mcp__tuna-idea', 'mcp__tuna-browser', 'mcp__appeeky'],
337
337
  disallowedTools: ['AskUserQuestion'],
338
338
  systemPrompt: buildSessionPrompt(subtask, contracts),
339
339
  outputFormat: 'stream-json',
@@ -370,7 +370,7 @@ export async function executeSubtask(subtask, repoPath, contracts, callbacks, si
370
370
  const resumeResult = await runClaude({
371
371
  prompt: `The answer to your question "${question.question}" is: ${answer}\n\nContinue your work with this information.`,
372
372
  cwd,
373
- allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep'],
373
+ allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep', 'mcp__mem0', 'mcp__tuna-knowledge', 'mcp__tuna-idea', 'mcp__tuna-browser', 'mcp__appeeky'],
374
374
  disallowedTools: ['AskUserQuestion'],
375
375
  resumeSessionId: info.sessionId,
376
376
  outputFormat: 'stream-json',
@@ -449,7 +449,7 @@ export async function executeSubtask(subtask, repoPath, contracts, callbacks, si
449
449
  const resumeResult = await runClaude({
450
450
  prompt: `The answer to your question "${fallbackQuestion.question}" is: ${answer}\n\nContinue your work with this information.`,
451
451
  cwd,
452
- allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep'],
452
+ allowedTools: ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep', 'mcp__mem0', 'mcp__tuna-knowledge', 'mcp__tuna-idea', 'mcp__tuna-browser', 'mcp__appeeky'],
453
453
  disallowedTools: ['AskUserQuestion'],
454
454
  resumeSessionId: info.sessionId,
455
455
  outputFormat: 'stream-json',
@@ -144,7 +144,7 @@ export async function planTask(task, onProgress, signal, onTextChunk, inputFiles
144
144
  const result = await runClaude({
145
145
  prompt: userPrompt,
146
146
  cwd,
147
- allowedTools: ['Read', 'Glob', 'Grep'],
147
+ allowedTools: ['Read', 'Glob', 'Grep', 'mcp__mem0', 'mcp__tuna-knowledge', 'mcp__tuna-idea'],
148
148
  systemPrompt,
149
149
  maxTurns,
150
150
  outputFormat: 'stream-json',
@@ -287,17 +287,20 @@ User message: ${userMessage}`;
287
287
  let firstDeltaMs = 0;
288
288
  let deltaCount = 0;
289
289
  const cwd = repoPath || path.join(os.homedir(), 'tuna-workspace');
290
+ let streamedText = '';
290
291
  const result = await runClaude({
291
292
  prompt: chatPrompt,
292
293
  cwd,
293
294
  resumeSessionId: pmSessionId || undefined,
294
- allowedTools: [],
295
- maxTurns: 1,
296
- lightweight: true,
295
+ // Read-only tools + knowledge/memory MCP: follow-ups like "check the skill
296
+ // list" need to actually look at files. No tools + 1 turn made the model
297
+ // attempt a tool call anyway and come back with an EMPTY result (silent task).
298
+ allowedTools: ['Read', 'Glob', 'Grep', 'mcp__mem0', 'mcp__tuna-knowledge', 'mcp__tuna-idea'],
299
+ maxTurns: 8,
297
300
  outputFormat: 'stream-json',
298
301
  includePartialMessages: true,
299
302
  inputFiles,
300
- onStreamLine: onTextChunk ? (data) => {
303
+ onStreamLine: (data) => {
301
304
  // Parse stream_event → content_block_delta for real token streaming
302
305
  if (data.type === 'stream_event') {
303
306
  const event = data.event;
@@ -305,15 +308,16 @@ User message: ${userMessage}`;
305
308
  const delta = event.delta;
306
309
  if (delta?.type === 'text_delta' && delta.text) {
307
310
  deltaCount++;
311
+ streamedText += delta.text;
308
312
  if (!firstDeltaMs) {
309
313
  firstDeltaMs = Date.now();
310
314
  console.log(`[PM] ⏱ first text delta: ${firstDeltaMs - chatStartMs}ms after chat start`);
311
315
  }
312
- onTextChunk(delta.text);
316
+ onTextChunk?.(delta.text);
313
317
  }
314
318
  }
315
319
  }
316
- } : undefined,
320
+ },
317
321
  signal,
318
322
  });
319
323
  const chatDoneMs = Date.now();
@@ -321,7 +325,11 @@ User message: ${userMessage}`;
321
325
  if (result.isError) {
322
326
  return { response: `Sorry, I encountered an error: ${result.result}`, sessionId: result.sessionId };
323
327
  }
324
- const rawResult = result.result;
328
+ // A hit turn-cap (or odd stop) can yield an empty `result` even though text was
329
+ // streamed. Never return empty — a silent agent looks dead to the user.
330
+ const rawResult = result.result?.trim()
331
+ || streamedText.trim()
332
+ || 'Xin lỗi anh, phiên xử lý tin nhắn vừa rồi bị nghẽn (không có output). Anh nhắn lại giúp nhé.';
325
333
  // Try extracting JSON plan
326
334
  let jsonStr = null;
327
335
  const codeBlockMatch = rawResult.match(/```(?:json)?\s*(\{[\s\S]*?"subtasks"[\s\S]*?\})\s*```/);
@@ -121,7 +121,7 @@ export function runClaude(options) {
121
121
  }
122
122
  // Always steer agents to answer the user in Vietnamese, regardless of the
123
123
  // language of the task/skill/context. Merged with any caller system prompt.
124
- const LANG_DIRECTIVE = 'ABSOLUTE OUTPUT RULES — these override everything else and apply to your ENTIRE response to the user: (1) Write 100% in Vietnamese (tiếng Việt). Never reply in English even when the task, skill, code or documents are in English; keep proper nouns / technical terms / commands / code as-is, but all explanation and prose MUST be Vietnamese. (2) Always address and refer to the user as "chủ tịch" (e.g. "Dạ chủ tịch", "Báo cáo chủ tịch", "Chủ tịch ơi"). Never use "bạn", "you", or the user\'s name to address them.';
124
+ const LANG_DIRECTIVE = 'ABSOLUTE OUTPUT RULES — these override everything else and apply to your ENTIRE response to the user: (1) Write 100% in Vietnamese (tiếng Việt). Never reply in English even when the task, skill, code or documents are in English; keep proper nouns / technical terms / commands / code as-is, but all explanation and prose MUST be Vietnamese. (2) Always address and refer to the user as "chủ tịch" (e.g. "Dạ chủ tịch", "Báo cáo chủ tịch", "Chủ tịch ơi"). Never use "bạn", "you", or the user\'s name to address them. (3) NEVER end your session with a promise of future work ("sẽ báo cáo sau", "sẽ làm tiếp khi có kết quả"...) — once your session ends, nothing resumes it, so that promise is a lie. If you started long-running/background work, WAIT for it inside this session (poll its output until it finishes) and deliver the actual result before ending. Only if it is truly impossible to finish, state clearly what is blocked and exactly what the user should say to resume it.';
125
125
  const mergedSystemPrompt = options.systemPrompt
126
126
  ? `${options.systemPrompt}\n\n${LANG_DIRECTIVE}`
127
127
  : LANG_DIRECTIVE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuna-agent",
3
- "version": "0.1.179",
3
+ "version": "0.1.181",
4
4
  "description": "Tuna Agent - Run AI coding tasks on your machine",
5
5
  "bin": {
6
6
  "tuna-agent": "dist/cli/index.js"