tuna-agent 0.1.6 → 0.1.8
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.
|
@@ -51,6 +51,7 @@ export class ClaudeCodeAdapter {
|
|
|
51
51
|
if (task.mode === 'agent_team') {
|
|
52
52
|
console.log(`[ClaudeCode] Agent Team mode — direct chat with Claude CLI`);
|
|
53
53
|
ws.sendProgress(task.id, 'executing', { startedAt: new Date().toISOString() });
|
|
54
|
+
ws.sendSubtaskStart(task.id, { id: 'agent-team', role: 'agent', description: task.description });
|
|
54
55
|
const MAX_ROUNDS = 50;
|
|
55
56
|
let sessionId;
|
|
56
57
|
let totalDurationMs = 0;
|
|
@@ -202,6 +203,11 @@ export class ClaudeCodeAdapter {
|
|
|
202
203
|
durationMs: totalDurationMs,
|
|
203
204
|
sessionId,
|
|
204
205
|
});
|
|
206
|
+
// Workflow tasks: no follow-up needed — workflow engine handles next step
|
|
207
|
+
if (task.source === 'workflow') {
|
|
208
|
+
console.log(`[ClaudeCode] Workflow task done — skipping follow-up wait`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
205
211
|
// Save state for resume after potential timeout or daemon restart
|
|
206
212
|
savePMState({
|
|
207
213
|
taskId: task.id,
|
|
@@ -26,8 +26,8 @@ export interface VideoRecord {
|
|
|
26
26
|
aspectRatio?: string;
|
|
27
27
|
}
|
|
28
28
|
export declare function handleGetHistory(ws: AgentWebSocketClient, code: string, taskId: string): void;
|
|
29
|
-
export declare function handleGenerateIdeas(ws: AgentWebSocketClient, code: string, taskId: string, topic: string): Promise<void>;
|
|
30
|
-
export declare function handleGenerateScript(ws: AgentWebSocketClient, code: string, taskId: string, idea: string, topic: string, style?: string, duration?: number, language?: string): Promise<void>;
|
|
29
|
+
export declare function handleGenerateIdeas(ws: AgentWebSocketClient, code: string, taskId: string, topic: string, styleName?: string, styleDesc?: string, language?: string): Promise<void>;
|
|
30
|
+
export declare function handleGenerateScript(ws: AgentWebSocketClient, code: string, taskId: string, idea: string, topic: string, style?: string, duration?: number, language?: string, styleName?: string, styleGuidance?: string): Promise<void>;
|
|
31
31
|
export declare function handleRetryVideo(ws: AgentWebSocketClient, code: string, taskId: string, videoId: string): void;
|
|
32
32
|
export declare function handleGenerateScene(ws: AgentWebSocketClient, code: string, taskId: string, sceneIdx: number, prompt: string, aspectRatio: string): Promise<void>;
|
|
33
33
|
export declare function handleGenerateScenes(ws: AgentWebSocketClient, code: string, taskId: string, scenes: Array<{
|
|
@@ -64,7 +64,7 @@ function hasContentCreator() {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
// ─── Handler: generate_ideas ──────────────────────────────────────────────────
|
|
67
|
-
export async function handleGenerateIdeas(ws, code, taskId, topic) {
|
|
67
|
+
export async function handleGenerateIdeas(ws, code, taskId, topic, styleName, styleDesc, language) {
|
|
68
68
|
if (!hasContentCreator()) {
|
|
69
69
|
const error = 'content-creator agent not found on this machine';
|
|
70
70
|
console.error(`[generate_ideas] ${error}`);
|
|
@@ -72,20 +72,33 @@ export async function handleGenerateIdeas(ws, code, taskId, topic) {
|
|
|
72
72
|
ws.sendExtensionDone(code, taskId, { error });
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
const
|
|
75
|
+
const resolvedLang = language || 'vi';
|
|
76
|
+
const langInstruction = resolvedLang === 'vi'
|
|
77
|
+
? `You always write titles in Vietnamese that feel native, not translated.`
|
|
78
|
+
: `You always write titles in English that feel natural and engaging.`;
|
|
79
|
+
const systemParts = [
|
|
76
80
|
`You are a top-tier content strategist and viral video expert specializing in YouTube Shorts and TikTok.`,
|
|
77
81
|
`You deeply understand viral hooks, storytelling patterns, and audience psychology.`,
|
|
78
82
|
`You know what makes people stop scrolling: curiosity gaps, emotional triggers, POV format, plot twists, listicles, and controversial takes.`,
|
|
79
|
-
|
|
80
|
-
]
|
|
83
|
+
langInstruction,
|
|
84
|
+
];
|
|
85
|
+
if (styleName) {
|
|
86
|
+
systemParts.push(`The video style is "${styleName}"${styleDesc ? `: ${styleDesc}` : ''}.`);
|
|
87
|
+
systemParts.push(`Generate ideas that are specifically suited for this style.`);
|
|
88
|
+
}
|
|
89
|
+
const systemPrompt = systemParts.join(' ');
|
|
90
|
+
const langReq = resolvedLang === 'vi'
|
|
91
|
+
? `- Titles must be in Vietnamese, natural and engaging`
|
|
92
|
+
: `- Titles must be in English, natural and engaging`;
|
|
81
93
|
const prompt = [
|
|
82
94
|
`Generate exactly 5 viral YouTube Shorts video ideas for the topic: "${topic}".`,
|
|
83
95
|
``,
|
|
84
96
|
`Requirements:`,
|
|
85
97
|
`- Each idea is a catchy, scroll-stopping video title`,
|
|
86
98
|
`- Use proven viral patterns: POV, "X điều...", plot twist, emotional hook, controversial take`,
|
|
87
|
-
|
|
99
|
+
langReq,
|
|
88
100
|
`- Mix different angles/formats for variety`,
|
|
101
|
+
...(styleName ? [`- Ideas must fit the "${styleName}" video style${styleDesc ? ` (${styleDesc})` : ''}`] : []),
|
|
89
102
|
``,
|
|
90
103
|
`Respond with ONLY a JSON array of 5 strings. No explanation, no markdown, no wrapping.`,
|
|
91
104
|
`Example format: ["title 1","title 2","title 3","title 4","title 5"]`,
|
|
@@ -150,7 +163,7 @@ export async function handleGenerateIdeas(ws, code, taskId, topic) {
|
|
|
150
163
|
// 2. Run `claude -p <prompt> --append-system-prompt <template>` in lightweight mode
|
|
151
164
|
// 3. Stream text deltas back to extension
|
|
152
165
|
// 4. Return full script on completion
|
|
153
|
-
export async function handleGenerateScript(ws, code, taskId, idea, topic, style, duration, language) {
|
|
166
|
+
export async function handleGenerateScript(ws, code, taskId, idea, topic, style, duration, language, styleName, styleGuidance) {
|
|
154
167
|
if (!hasContentCreator()) {
|
|
155
168
|
const error = 'content-creator agent not found on this machine';
|
|
156
169
|
console.error(`[generate_script] ${error}`);
|
|
@@ -172,7 +185,7 @@ export async function handleGenerateScript(ws, code, taskId, idea, topic, style,
|
|
|
172
185
|
const resolvedStyle = style || 'short-form';
|
|
173
186
|
const resolvedDuration = duration || 60;
|
|
174
187
|
const resolvedLanguage = language || 'vi';
|
|
175
|
-
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)'}`);
|
|
176
189
|
const expandedTemplate = template
|
|
177
190
|
.replace(/\$ARGUMENTS/g, idea)
|
|
178
191
|
.replace(/\$IDEA/g, idea)
|
|
@@ -196,7 +209,12 @@ export async function handleGenerateScript(ws, code, taskId, idea, topic, style,
|
|
|
196
209
|
catch {
|
|
197
210
|
// Non-fatal — Claude will use N/A for characters
|
|
198
211
|
}
|
|
199
|
-
|
|
212
|
+
// Build style guidance section
|
|
213
|
+
let styleContext = '';
|
|
214
|
+
if (styleGuidance && styleGuidance.trim()) {
|
|
215
|
+
styleContext = `\n\n## VIDEO STYLE GUIDANCE (${styleName || resolvedStyle})\n\nFollow these style-specific instructions for tone, pacing, camera work, and visual direction. The output format MUST remain exactly as specified above — only the CONTENT within each section should reflect this guidance.\n\n${styleGuidance.trim()}`;
|
|
216
|
+
}
|
|
217
|
+
const systemPrompt = expandedTemplate + styleContext + (characterContext
|
|
200
218
|
? `\n\n## AVAILABLE CHARACTERS\n${characterContext}`
|
|
201
219
|
: '');
|
|
202
220
|
try {
|
package/dist/daemon/index.js
CHANGED
|
@@ -345,13 +345,13 @@ ${skillContent.slice(0, 15000)}`;
|
|
|
345
345
|
}
|
|
346
346
|
if (extTask === 'generate_ideas') {
|
|
347
347
|
(async () => {
|
|
348
|
-
await handleGenerateIdeas(ws, extCode, extTaskId, msg.topic);
|
|
348
|
+
await handleGenerateIdeas(ws, extCode, extTaskId, msg.topic, msg.styleName, msg.styleDesc, msg.language);
|
|
349
349
|
})();
|
|
350
350
|
break;
|
|
351
351
|
}
|
|
352
352
|
if (extTask === 'generate_script') {
|
|
353
353
|
(async () => {
|
|
354
|
-
await handleGenerateScript(ws, extCode, extTaskId, msg.idea, msg.topic, msg.style, msg.duration, msg.language);
|
|
354
|
+
await handleGenerateScript(ws, extCode, extTaskId, msg.idea, msg.topic, msg.style, msg.duration, msg.language, msg.styleName, msg.styleGuidance);
|
|
355
355
|
})();
|
|
356
356
|
break;
|
|
357
357
|
}
|
package/dist/mcp/setup.js
CHANGED
|
@@ -6,33 +6,43 @@ const __dirname = path.dirname(__filename);
|
|
|
6
6
|
const MCP_CONFIG_DIR = path.join(process.env.HOME || '', '.tuna-agent');
|
|
7
7
|
const MCP_CONFIG_PATH = path.join(MCP_CONFIG_DIR, 'mcp-config.json');
|
|
8
8
|
// Mem0 config from environment variables
|
|
9
|
-
|
|
9
|
+
// MEM0_SSH_HOST: "local" = run mem0-mcp directly, "user@host" = run via SSH, unset = disabled
|
|
10
|
+
const MEM0_SSH_HOST = process.env.MEM0_SSH_HOST;
|
|
10
11
|
const MEM0_SSH_PORT = process.env.MEM0_SSH_PORT || '22';
|
|
11
|
-
const MEM0_SSH_KEY = process.env.MEM0_SSH_KEY;
|
|
12
|
+
const MEM0_SSH_KEY = process.env.MEM0_SSH_KEY;
|
|
13
|
+
const MEM0_ENV_VARS = {
|
|
14
|
+
MEM0_API_BASE: 'http://127.0.0.1:8765',
|
|
15
|
+
MEM0_QDRANT_URL: 'http://127.0.0.1:6333',
|
|
16
|
+
MEM0_OLLAMA_URL: 'http://127.0.0.1:11434',
|
|
17
|
+
MEM0_EMBED_MODEL: 'mxbai-embed-large:latest',
|
|
18
|
+
MEM0_COLLECTION: 'openmemory',
|
|
19
|
+
MEM0_NEO4J_URL: 'bolt://127.0.0.1:7687',
|
|
20
|
+
MEM0_NEO4J_USER: 'neo4j',
|
|
21
|
+
MEM0_NEO4J_PASSWORD: 'mem0graph',
|
|
22
|
+
};
|
|
12
23
|
/**
|
|
13
24
|
* Build Mem0 MCP server config for an agent.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
25
|
+
* - MEM0_SSH_HOST="local": run mem0-mcp directly (Mem0 infra on same machine)
|
|
26
|
+
* - MEM0_SSH_HOST="user@host": run mem0-mcp via SSH stdio
|
|
27
|
+
* - MEM0_SSH_HOST unset: disabled
|
|
16
28
|
*/
|
|
17
29
|
function buildMem0McpConfig(agentName) {
|
|
18
30
|
if (!MEM0_SSH_HOST)
|
|
19
31
|
return null;
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'MEM0_NEO4J_PASSWORD=mem0graph',
|
|
30
|
-
].join(' ');
|
|
32
|
+
const envWithUser = { ...MEM0_ENV_VARS, MEM0_USER_ID: agentName };
|
|
33
|
+
if (MEM0_SSH_HOST === 'local') {
|
|
34
|
+
return {
|
|
35
|
+
command: 'mem0-mcp',
|
|
36
|
+
args: [],
|
|
37
|
+
env: envWithUser,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const envString = Object.entries(envWithUser).map(([k, v]) => `${k}=${v}`).join(' ');
|
|
31
41
|
const args = ['-p', MEM0_SSH_PORT, '-o', 'StrictHostKeyChecking=no'];
|
|
32
42
|
if (MEM0_SSH_KEY) {
|
|
33
43
|
args.push('-i', MEM0_SSH_KEY);
|
|
34
44
|
}
|
|
35
|
-
args.push(MEM0_SSH_HOST, `${
|
|
45
|
+
args.push(MEM0_SSH_HOST, `${envString} mem0-mcp`);
|
|
36
46
|
return { command: 'ssh', args };
|
|
37
47
|
}
|
|
38
48
|
/**
|