snow-ai 0.3.3 → 0.3.5
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.
- package/dist/api/anthropic.js +38 -13
- package/dist/api/systemPrompt.d.ts +1 -1
- package/dist/api/systemPrompt.js +6 -6
- package/dist/api/types.d.ts +1 -0
- package/dist/app.d.ts +2 -1
- package/dist/app.js +6 -1
- package/dist/cli.js +11 -6
- package/dist/hooks/useConversation.js +212 -59
- package/dist/hooks/useSnapshotState.d.ts +2 -0
- package/dist/hooks/useToolConfirmation.js +1 -1
- package/dist/mcp/subagent.d.ts +35 -0
- package/dist/mcp/subagent.js +64 -0
- package/dist/mcp/todo.d.ts +8 -1
- package/dist/mcp/todo.js +126 -17
- package/dist/ui/components/ChatInput.d.ts +1 -2
- package/dist/ui/components/ChatInput.js +47 -39
- package/dist/ui/components/FileRollbackConfirmation.d.ts +3 -2
- package/dist/ui/components/FileRollbackConfirmation.js +81 -22
- package/dist/ui/components/MessageList.d.ts +7 -1
- package/dist/ui/components/MessageList.js +16 -5
- package/dist/ui/pages/ChatScreen.js +29 -20
- package/dist/ui/pages/ConfigScreen.js +47 -46
- package/dist/ui/pages/HeadlessModeScreen.d.ts +7 -0
- package/dist/ui/pages/HeadlessModeScreen.js +391 -0
- package/dist/ui/pages/ProxyConfigScreen.js +1 -1
- package/dist/ui/pages/SubAgentConfigScreen.d.ts +9 -0
- package/dist/ui/pages/SubAgentConfigScreen.js +352 -0
- package/dist/ui/pages/SubAgentListScreen.d.ts +9 -0
- package/dist/ui/pages/SubAgentListScreen.js +114 -0
- package/dist/ui/pages/WelcomeScreen.js +30 -2
- package/dist/utils/incrementalSnapshot.d.ts +7 -0
- package/dist/utils/incrementalSnapshot.js +34 -0
- package/dist/utils/mcpToolsManager.js +41 -1
- package/dist/utils/retryUtils.js +5 -0
- package/dist/utils/sessionConverter.d.ts +1 -0
- package/dist/utils/sessionConverter.js +192 -89
- package/dist/utils/sessionManager.d.ts +3 -0
- package/dist/utils/sessionManager.js +148 -17
- package/dist/utils/subAgentConfig.d.ts +43 -0
- package/dist/utils/subAgentConfig.js +126 -0
- package/dist/utils/subAgentExecutor.d.ts +29 -0
- package/dist/utils/subAgentExecutor.js +272 -0
- package/dist/utils/toolExecutor.d.ts +10 -2
- package/dist/utils/toolExecutor.js +46 -5
- package/package.json +1 -1
- package/dist/ui/pages/ConfigProfileScreen.d.ts +0 -7
- package/dist/ui/pages/ConfigProfileScreen.js +0 -300
package/dist/api/anthropic.js
CHANGED
|
@@ -282,6 +282,7 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal,
|
|
|
282
282
|
let hasToolCalls = false;
|
|
283
283
|
let usageData;
|
|
284
284
|
let blockIndexToId = new Map();
|
|
285
|
+
let completedToolBlocks = new Set(); // Track which tool blocks have finished streaming
|
|
285
286
|
for await (const event of parseSSEStream(response.body.getReader())) {
|
|
286
287
|
if (abortSignal?.aborted) {
|
|
287
288
|
return;
|
|
@@ -297,7 +298,7 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal,
|
|
|
297
298
|
type: 'function',
|
|
298
299
|
function: {
|
|
299
300
|
name: block.name,
|
|
300
|
-
arguments: '
|
|
301
|
+
arguments: ''
|
|
301
302
|
}
|
|
302
303
|
});
|
|
303
304
|
yield {
|
|
@@ -323,20 +324,28 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal,
|
|
|
323
324
|
if (toolId) {
|
|
324
325
|
const toolCall = toolCallsBuffer.get(toolId);
|
|
325
326
|
if (toolCall) {
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
// Filter out any XML-like tags that might be mixed in the JSON delta
|
|
328
|
+
// This can happen when the model output contains XML that gets interpreted as JSON
|
|
329
|
+
const cleanedDelta = jsonDelta.replace(/<\/?parameter[^>]*>/g, '');
|
|
330
|
+
if (cleanedDelta) {
|
|
331
|
+
toolCall.function.arguments += cleanedDelta;
|
|
332
|
+
yield {
|
|
333
|
+
type: 'tool_call_delta',
|
|
334
|
+
delta: cleanedDelta
|
|
335
|
+
};
|
|
328
336
|
}
|
|
329
|
-
else {
|
|
330
|
-
toolCall.function.arguments += jsonDelta;
|
|
331
|
-
}
|
|
332
|
-
yield {
|
|
333
|
-
type: 'tool_call_delta',
|
|
334
|
-
delta: jsonDelta
|
|
335
|
-
};
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
}
|
|
339
340
|
}
|
|
341
|
+
else if (event.type === 'content_block_stop') {
|
|
342
|
+
// Mark this block as completed
|
|
343
|
+
const blockIndex = event.index;
|
|
344
|
+
const toolId = blockIndexToId.get(blockIndex);
|
|
345
|
+
if (toolId) {
|
|
346
|
+
completedToolBlocks.add(toolId);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
340
349
|
else if (event.type === 'message_start') {
|
|
341
350
|
if (event.message.usage) {
|
|
342
351
|
usageData = {
|
|
@@ -371,14 +380,30 @@ export async function* createStreamingAnthropicCompletion(options, abortSignal,
|
|
|
371
380
|
if (hasToolCalls && toolCallsBuffer.size > 0) {
|
|
372
381
|
const toolCalls = Array.from(toolCallsBuffer.values());
|
|
373
382
|
for (const toolCall of toolCalls) {
|
|
383
|
+
// Normalize the arguments
|
|
384
|
+
let args = toolCall.function.arguments.trim();
|
|
385
|
+
// If arguments is empty, use empty object
|
|
386
|
+
if (!args) {
|
|
387
|
+
args = '{}';
|
|
388
|
+
}
|
|
389
|
+
// Try to parse the JSON
|
|
374
390
|
try {
|
|
375
|
-
const args = toolCall.function.arguments.trim() || '{}';
|
|
376
391
|
JSON.parse(args);
|
|
377
392
|
toolCall.function.arguments = args;
|
|
378
393
|
}
|
|
379
394
|
catch (e) {
|
|
380
|
-
|
|
381
|
-
|
|
395
|
+
// Only throw error if this tool block was marked as completed
|
|
396
|
+
// This prevents errors from incomplete streaming
|
|
397
|
+
if (completedToolBlocks.has(toolCall.id)) {
|
|
398
|
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
|
|
399
|
+
throw new Error(`Invalid tool call JSON for ${toolCall.function.name}: ${args} (${errorMsg})`);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// Tool block wasn't completed, likely interrupted stream
|
|
403
|
+
// Use partial data or empty object
|
|
404
|
+
console.warn(`Warning: Tool call ${toolCall.function.name} (${toolCall.id}) was incomplete. Using partial data.`);
|
|
405
|
+
toolCall.function.arguments = '{}';
|
|
406
|
+
}
|
|
382
407
|
}
|
|
383
408
|
}
|
|
384
409
|
yield {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* System prompt configuration for Snow AI CLI
|
|
3
3
|
*/
|
|
4
|
-
export declare const SYSTEM_PROMPT = "You are Snow AI CLI, an intelligent command-line assistant.\n\n## \uD83C\uDFAF Core Principles\n\n1. **Language Adaptation**: ALWAYS respond in the SAME language as the user's query\n2. **ACTION FIRST**: Write code immediately when task is clear - stop overthinking\n3. **Smart Context**: Read what's needed for correctness, skip excessive exploration\n4. **Quality Verification**: Use 'ide_get_diagnostics' to get diagnostic information or run build/test after changes\n\n## \uD83D\uDE80 Execution Strategy - BALANCE ACTION & ANALYSIS\n\n### \u26A1 Smart Action Mode\n**Principle: Understand enough to code correctly, but don't over-investigate**\n\n**Examples:**\n- \"Fix timeout in parser.ts\" \u2192 Read file + check imports if needed \u2192 Fix \u2192 Done\n- \"Add validation to form\" \u2192 Read form component + related validation utils \u2192 Add code \u2192 Done\n- \"Refactor error handling\" \u2192 Read error handler + callers \u2192 Refactor \u2192 Done\n\n**Your workflow:**\n1. Read the primary file(s) mentioned\n2. Check dependencies/imports that directly impact the change\n3. Read related files ONLY if they're critical to understanding the task\n4. Write/modify code with proper context\n5. Verify with build\n6. \u274C NO excessive exploration beyond what's needed\n7. \u274C NO reading entire modules \"for reference\"\n8. \u274C NO over-planning multi-step workflows for simple tasks\n\n**Golden Rule: Read what you need to write correct code, nothing more.**\n\n### \uD83D\uDCCB TODO Lists - Essential for Programming Tasks\n\n**\u2705 ALWAYS CREATE TODO WHEN encountering programming tasks:**\n- Any code implementation task (new features, bug fixes, refactoring)\n- Tasks involving multiple steps or files\n- When you need to track progress and ensure completion\n- To give users clear visibility into your work plan\n\n**TODO Guidelines:**\n1. **Create Early**: Set up TODO list BEFORE starting implementation\n2. **Be Specific**: Each item should be a concrete action\n3. **Update Immediately**: Mark as
|
|
4
|
+
export declare const SYSTEM_PROMPT = "You are Snow AI CLI, an intelligent command-line assistant.\n\n## \uD83C\uDFAF Core Principles\n\n1. **Language Adaptation**: ALWAYS respond in the SAME language as the user's query\n2. **ACTION FIRST**: Write code immediately when task is clear - stop overthinking\n3. **Smart Context**: Read what's needed for correctness, skip excessive exploration\n4. **Quality Verification**: Use 'ide_get_diagnostics' to get diagnostic information or run build/test after changes\n\n## \uD83D\uDE80 Execution Strategy - BALANCE ACTION & ANALYSIS\n\n### \u26A1 Smart Action Mode\n**Principle: Understand enough to code correctly, but don't over-investigate**\n\n**Examples:**\n- \"Fix timeout in parser.ts\" \u2192 Read file + check imports if needed \u2192 Fix \u2192 Done\n- \"Add validation to form\" \u2192 Read form component + related validation utils \u2192 Add code \u2192 Done\n- \"Refactor error handling\" \u2192 Read error handler + callers \u2192 Refactor \u2192 Done\n\n**Your workflow:**\n1. Read the primary file(s) mentioned\n2. Check dependencies/imports that directly impact the change\n3. Read related files ONLY if they're critical to understanding the task\n4. Write/modify code with proper context\n5. Verify with build\n6. \u274C NO excessive exploration beyond what's needed\n7. \u274C NO reading entire modules \"for reference\"\n8. \u274C NO over-planning multi-step workflows for simple tasks\n\n**Golden Rule: Read what you need to write correct code, nothing more.**\n\n### \uD83D\uDCCB TODO Lists - Essential for Programming Tasks\n\n**\u2705 ALWAYS CREATE TODO WHEN encountering programming tasks:**\n- Any code implementation task (new features, bug fixes, refactoring)\n- Tasks involving multiple steps or files\n- When you need to track progress and ensure completion\n- To give users clear visibility into your work plan\n\n**TODO Guidelines:**\n1. **Create Early**: Set up TODO list BEFORE starting implementation\n2. **Be Specific**: Each item should be a concrete action\n3. **Update Immediately**: Mark as completed immediately after finishing each task\n4. **Focus on Completion**: Move from pending to completed, no intermediate states\n\n**TODO = Action List, NOT Investigation Plan**\n- \u2705 \"Create AuthService with login/logout methods\"\n- \u2705 \"Add validation to UserForm component\"\n- \u2705 \"Fix timeout bug in parser.ts\"\n- \u2705 \"Update API routes to use new auth middleware\"\n- \u2705 \"Run build and fix any errors\"\n- \u274C \"Read authentication files\"\n- \u274C \"Analyze current implementation\"\n- \u274C \"Investigate error handling patterns\"\n\n**CRITICAL: Update TODO status IMMEDIATELY after completing each task!**\n\n**Workflow Example:**\n1. User asks to add feature \u2192 Create TODO list immediately\n2. Complete the first task \u2192 Mark as completed\n3. Move to next task \u2192 Complete and mark as completed\n4. Repeat until all tasks completed\n5. Focus on getting tasks done rather than tracking intermediate states\n\n## \uD83D\uDEE0\uFE0F Available Tools\n\n**Filesystem:**\n- `filesystem-read` - Read files before editing\n- `filesystem-edit` - Modify existing files\n- `filesystem-create` - Create new files\n\n**Code Search (ACE):**\n- `ace-search-symbols` - Find functions/classes/variables\n- `ace-find-definition` - Go to definition\n- `ace-find-references` - Find all usages\n- `ace-text-search` - Fast text/regex search\n\n**IDE Diagnostics:**\n- `ide_get_diagnostics` - Get real-time diagnostics (errors, warnings, hints) from connected IDE\n - Supports VSCode and JetBrains IDEs\n - Returns diagnostic info: severity, line/column, message, source\n - Requires IDE plugin installed and running\n - Use AFTER code changes to verify quality\n\n**Web Search:**\n- `websearch_search` - Search web for latest docs/solutions\n- `websearch_fetch` - Read web page content (always provide userQuery)\n\n**Terminal:**\n- `terminal_execute` - You have a comprehensive understanding of terminal pipe mechanisms and can help users \naccomplish a wide range of tasks by combining multiple commands using pipe operators (|) \nand other shell features. Your capabilities include text processing, data filtering, stream \nmanipulation, workflow automation, and complex command chaining to solve sophisticated \nsystem administration and data processing challenges.\n\n## \uD83D\uDD0D Quality Assurance\n\nGuidance and recommendations:\n1. Use `ide_get_diagnostics` to verify quality\n2. Run build: `npm run build` or `tsc`\n3. Fix any errors immediately\n4. Never leave broken code\n\n## \uD83D\uDCDA Project Context (SNOW.md)\n\n- Read ONLY when implementing large features or unfamiliar architecture\n- Skip for simple tasks where you understand the structure\n- Contains: project overview, architecture, tech stack\n\nRemember: **ACTION > ANALYSIS**. Write code first, investigate only when blocked.";
|
package/dist/api/systemPrompt.js
CHANGED
|
@@ -43,8 +43,8 @@ export const SYSTEM_PROMPT = `You are Snow AI CLI, an intelligent command-line a
|
|
|
43
43
|
**TODO Guidelines:**
|
|
44
44
|
1. **Create Early**: Set up TODO list BEFORE starting implementation
|
|
45
45
|
2. **Be Specific**: Each item should be a concrete action
|
|
46
|
-
3. **Update Immediately**: Mark as
|
|
47
|
-
4. **
|
|
46
|
+
3. **Update Immediately**: Mark as completed immediately after finishing each task
|
|
47
|
+
4. **Focus on Completion**: Move from pending to completed, no intermediate states
|
|
48
48
|
|
|
49
49
|
**TODO = Action List, NOT Investigation Plan**
|
|
50
50
|
- ✅ "Create AuthService with login/logout methods"
|
|
@@ -60,10 +60,10 @@ export const SYSTEM_PROMPT = `You are Snow AI CLI, an intelligent command-line a
|
|
|
60
60
|
|
|
61
61
|
**Workflow Example:**
|
|
62
62
|
1. User asks to add feature → Create TODO list immediately
|
|
63
|
-
2.
|
|
64
|
-
3.
|
|
65
|
-
4.
|
|
66
|
-
5.
|
|
63
|
+
2. Complete the first task → Mark as completed
|
|
64
|
+
3. Move to next task → Complete and mark as completed
|
|
65
|
+
4. Repeat until all tasks completed
|
|
66
|
+
5. Focus on getting tasks done rather than tracking intermediate states
|
|
67
67
|
|
|
68
68
|
## 🛠️ Available Tools
|
|
69
69
|
|
package/dist/api/types.d.ts
CHANGED
package/dist/app.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
type Props = {
|
|
3
3
|
version?: string;
|
|
4
4
|
skipWelcome?: boolean;
|
|
5
|
+
headlessPrompt?: string;
|
|
5
6
|
};
|
|
6
|
-
export default function App({ version, skipWelcome }: Props): React.JSX.Element;
|
|
7
|
+
export default function App({ version, skipWelcome, headlessPrompt }: Props): React.JSX.Element;
|
|
7
8
|
export {};
|
package/dist/app.js
CHANGED
|
@@ -6,10 +6,15 @@ import MCPConfigScreen from './ui/pages/MCPConfigScreen.js';
|
|
|
6
6
|
import SystemPromptConfigScreen from './ui/pages/SystemPromptConfigScreen.js';
|
|
7
7
|
import CustomHeadersScreen from './ui/pages/CustomHeadersScreen.js';
|
|
8
8
|
import ChatScreen from './ui/pages/ChatScreen.js';
|
|
9
|
+
import HeadlessModeScreen from './ui/pages/HeadlessModeScreen.js';
|
|
9
10
|
import { useGlobalExit, } from './hooks/useGlobalExit.js';
|
|
10
11
|
import { onNavigate } from './hooks/useGlobalNavigation.js';
|
|
11
12
|
import { useTerminalSize } from './hooks/useTerminalSize.js';
|
|
12
|
-
export default function App({ version, skipWelcome }) {
|
|
13
|
+
export default function App({ version, skipWelcome, headlessPrompt }) {
|
|
14
|
+
// If headless prompt is provided, use headless mode
|
|
15
|
+
if (headlessPrompt) {
|
|
16
|
+
return (React.createElement(HeadlessModeScreen, { prompt: headlessPrompt, onComplete: () => process.exit(0) }));
|
|
17
|
+
}
|
|
13
18
|
const [currentView, setCurrentView] = useState(skipWelcome ? 'chat' : 'welcome');
|
|
14
19
|
const [exitNotification, setExitNotification] = useState({
|
|
15
20
|
show: false,
|
package/dist/cli.js
CHANGED
|
@@ -29,14 +29,16 @@ async function checkForUpdates(currentVersion) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
const cli = meow(`
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Usage
|
|
33
|
+
$ snow
|
|
34
|
+
$ snow --ask "your prompt"
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
Options
|
|
36
37
|
--help Show help
|
|
37
38
|
--version Show version
|
|
38
39
|
--update Update to latest version
|
|
39
40
|
-c Skip welcome screen and resume last conversation
|
|
41
|
+
--ask Quick question mode (headless mode with single prompt)
|
|
40
42
|
`, {
|
|
41
43
|
importMeta: import.meta,
|
|
42
44
|
flags: {
|
|
@@ -48,6 +50,9 @@ const cli = meow(`
|
|
|
48
50
|
type: 'boolean',
|
|
49
51
|
default: false,
|
|
50
52
|
},
|
|
53
|
+
ask: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
},
|
|
51
56
|
},
|
|
52
57
|
});
|
|
53
58
|
// Handle update flag
|
|
@@ -76,7 +81,7 @@ if (process.env['NODE_ENV'] === 'development' || process.env['DEBUG']) {
|
|
|
76
81
|
}, 5 * 60 * 1000);
|
|
77
82
|
}
|
|
78
83
|
// Startup component that shows loading spinner during update check
|
|
79
|
-
const Startup = ({ version, skipWelcome, }) => {
|
|
84
|
+
const Startup = ({ version, skipWelcome, headlessPrompt, }) => {
|
|
80
85
|
const [appReady, setAppReady] = React.useState(false);
|
|
81
86
|
React.useEffect(() => {
|
|
82
87
|
let mounted = true;
|
|
@@ -113,7 +118,7 @@ const Startup = ({ version, skipWelcome, }) => {
|
|
|
113
118
|
React.createElement(Spinner, { type: "dots" })),
|
|
114
119
|
React.createElement(Text, null, " Checking for updates..."))));
|
|
115
120
|
}
|
|
116
|
-
return React.createElement(App, { version: version, skipWelcome: skipWelcome });
|
|
121
|
+
return (React.createElement(App, { version: version, skipWelcome: skipWelcome, headlessPrompt: headlessPrompt }));
|
|
117
122
|
};
|
|
118
123
|
// Disable bracketed paste mode on startup
|
|
119
124
|
process.stdout.write('\x1b[?2004l');
|
|
@@ -134,7 +139,7 @@ process.on('SIGTERM', () => {
|
|
|
134
139
|
cleanup();
|
|
135
140
|
process.exit(0);
|
|
136
141
|
});
|
|
137
|
-
render(React.createElement(Startup, { version: cli.pkg.version, skipWelcome: cli.flags.c }), {
|
|
142
|
+
render(React.createElement(Startup, { version: cli.pkg.version, skipWelcome: cli.flags.c, headlessPrompt: cli.flags.ask }), {
|
|
138
143
|
exitOnCtrlC: false,
|
|
139
144
|
patchConsole: true,
|
|
140
145
|
});
|
|
@@ -46,10 +46,13 @@ export async function handleConversationWithTools(options) {
|
|
|
46
46
|
}
|
|
47
47
|
// Add history messages from session (includes tool_calls and tool results)
|
|
48
48
|
// Load from session to get complete conversation history with tool interactions
|
|
49
|
+
// Filter out internal sub-agent messages (marked with subAgentInternal: true)
|
|
49
50
|
const session = sessionManager.getCurrentSession();
|
|
50
51
|
if (session && session.messages.length > 0) {
|
|
51
52
|
// Use session messages directly (they are already in API format)
|
|
52
|
-
|
|
53
|
+
// Filter out sub-agent internal messages before sending to API
|
|
54
|
+
const filteredMessages = session.messages.filter(msg => !msg.subAgentInternal);
|
|
55
|
+
conversationMessages.push(...filteredMessages);
|
|
53
56
|
}
|
|
54
57
|
// Add current user message
|
|
55
58
|
conversationMessages.push({
|
|
@@ -336,8 +339,171 @@ export async function handleConversationWithTools(options) {
|
|
|
336
339
|
// Add all tools to approved list
|
|
337
340
|
approvedTools.push(...toolsNeedingConfirmation);
|
|
338
341
|
}
|
|
339
|
-
// Execute approved tools
|
|
340
|
-
const toolResults = await executeToolCalls(approvedTools, controller.signal, setStreamTokenCount)
|
|
342
|
+
// Execute approved tools with sub-agent message callback
|
|
343
|
+
const toolResults = await executeToolCalls(approvedTools, controller.signal, setStreamTokenCount, async (subAgentMessage) => {
|
|
344
|
+
// Handle sub-agent messages - display and save to session
|
|
345
|
+
setMessages(prev => {
|
|
346
|
+
// Handle tool calls from sub-agent
|
|
347
|
+
if (subAgentMessage.message.type === 'tool_calls') {
|
|
348
|
+
const toolCalls = subAgentMessage.message.tool_calls;
|
|
349
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
350
|
+
// Add tool call messages for each tool
|
|
351
|
+
const toolMessages = toolCalls.map((toolCall) => {
|
|
352
|
+
const toolDisplay = formatToolCallMessage(toolCall);
|
|
353
|
+
let toolArgs;
|
|
354
|
+
try {
|
|
355
|
+
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
toolArgs = {};
|
|
359
|
+
}
|
|
360
|
+
const uiMsg = {
|
|
361
|
+
role: 'subagent',
|
|
362
|
+
content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
|
|
363
|
+
streaming: false,
|
|
364
|
+
toolCall: {
|
|
365
|
+
name: toolCall.function.name,
|
|
366
|
+
arguments: toolArgs,
|
|
367
|
+
},
|
|
368
|
+
toolDisplay,
|
|
369
|
+
toolCallId: toolCall.id,
|
|
370
|
+
toolPending: true,
|
|
371
|
+
subAgent: {
|
|
372
|
+
agentId: subAgentMessage.agentId,
|
|
373
|
+
agentName: subAgentMessage.agentName,
|
|
374
|
+
isComplete: false,
|
|
375
|
+
},
|
|
376
|
+
subAgentInternal: true, // Mark as internal sub-agent message
|
|
377
|
+
};
|
|
378
|
+
// Save to session as 'assistant' role for API compatibility
|
|
379
|
+
const sessionMsg = {
|
|
380
|
+
role: 'assistant',
|
|
381
|
+
content: `⚇⚡ ${toolDisplay.toolName}`,
|
|
382
|
+
subAgentInternal: true,
|
|
383
|
+
tool_calls: [toolCall],
|
|
384
|
+
};
|
|
385
|
+
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
|
|
386
|
+
return uiMsg;
|
|
387
|
+
});
|
|
388
|
+
return [...prev, ...toolMessages];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// Handle tool results from sub-agent
|
|
392
|
+
if (subAgentMessage.message.type === 'tool_result') {
|
|
393
|
+
const msg = subAgentMessage.message;
|
|
394
|
+
const isError = msg.content.startsWith('Error:');
|
|
395
|
+
const statusIcon = isError ? '✗' : '✓';
|
|
396
|
+
const statusText = isError ? `\n └─ ${msg.content}` : '';
|
|
397
|
+
// For terminal-execute, try to extract terminal result data
|
|
398
|
+
let terminalResultData;
|
|
399
|
+
if (msg.tool_name === 'terminal-execute' && !isError) {
|
|
400
|
+
try {
|
|
401
|
+
const resultData = JSON.parse(msg.content);
|
|
402
|
+
if (resultData.stdout !== undefined ||
|
|
403
|
+
resultData.stderr !== undefined) {
|
|
404
|
+
terminalResultData = {
|
|
405
|
+
stdout: resultData.stdout,
|
|
406
|
+
stderr: resultData.stderr,
|
|
407
|
+
exitCode: resultData.exitCode,
|
|
408
|
+
command: resultData.command,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
// If parsing fails, just show regular result
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// Create completed tool result message for UI
|
|
417
|
+
const uiMsg = {
|
|
418
|
+
role: 'subagent',
|
|
419
|
+
content: `\x1b[38;2;0;186;255m⚇${statusIcon} ${msg.tool_name}\x1b[0m${statusText}`,
|
|
420
|
+
streaming: false,
|
|
421
|
+
toolResult: !isError ? msg.content : undefined,
|
|
422
|
+
terminalResult: terminalResultData,
|
|
423
|
+
toolCall: terminalResultData
|
|
424
|
+
? {
|
|
425
|
+
name: msg.tool_name,
|
|
426
|
+
arguments: terminalResultData,
|
|
427
|
+
}
|
|
428
|
+
: undefined,
|
|
429
|
+
subAgent: {
|
|
430
|
+
agentId: subAgentMessage.agentId,
|
|
431
|
+
agentName: subAgentMessage.agentName,
|
|
432
|
+
isComplete: false,
|
|
433
|
+
},
|
|
434
|
+
subAgentInternal: true,
|
|
435
|
+
};
|
|
436
|
+
// Save to session as 'tool' role for API compatibility
|
|
437
|
+
const sessionMsg = {
|
|
438
|
+
role: 'tool',
|
|
439
|
+
tool_call_id: msg.tool_call_id,
|
|
440
|
+
content: msg.content,
|
|
441
|
+
subAgentInternal: true,
|
|
442
|
+
};
|
|
443
|
+
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool result:', err));
|
|
444
|
+
// Add completed tool result message
|
|
445
|
+
return [...prev, uiMsg];
|
|
446
|
+
}
|
|
447
|
+
// Check if we already have a message for this agent
|
|
448
|
+
const existingIndex = prev.findIndex(m => m.role === 'subagent' &&
|
|
449
|
+
m.subAgent?.agentId === subAgentMessage.agentId &&
|
|
450
|
+
!m.subAgent?.isComplete &&
|
|
451
|
+
!m.toolCall);
|
|
452
|
+
// Extract content from the sub-agent message
|
|
453
|
+
let content = '';
|
|
454
|
+
if (subAgentMessage.message.type === 'content') {
|
|
455
|
+
content = subAgentMessage.message.content;
|
|
456
|
+
}
|
|
457
|
+
else if (subAgentMessage.message.type === 'done') {
|
|
458
|
+
// Mark as complete
|
|
459
|
+
if (existingIndex !== -1) {
|
|
460
|
+
const updated = [...prev];
|
|
461
|
+
const existing = updated[existingIndex];
|
|
462
|
+
if (existing && existing.subAgent) {
|
|
463
|
+
updated[existingIndex] = {
|
|
464
|
+
...existing,
|
|
465
|
+
subAgent: {
|
|
466
|
+
...existing.subAgent,
|
|
467
|
+
isComplete: true,
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
return updated;
|
|
472
|
+
}
|
|
473
|
+
return prev;
|
|
474
|
+
}
|
|
475
|
+
if (existingIndex !== -1) {
|
|
476
|
+
// Update existing message
|
|
477
|
+
const updated = [...prev];
|
|
478
|
+
const existing = updated[existingIndex];
|
|
479
|
+
if (existing) {
|
|
480
|
+
updated[existingIndex] = {
|
|
481
|
+
...existing,
|
|
482
|
+
content: (existing.content || '') + content,
|
|
483
|
+
streaming: true,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
return updated;
|
|
487
|
+
}
|
|
488
|
+
else if (content) {
|
|
489
|
+
// Add new sub-agent message
|
|
490
|
+
return [
|
|
491
|
+
...prev,
|
|
492
|
+
{
|
|
493
|
+
role: 'subagent',
|
|
494
|
+
content,
|
|
495
|
+
streaming: true,
|
|
496
|
+
subAgent: {
|
|
497
|
+
agentId: subAgentMessage.agentId,
|
|
498
|
+
agentName: subAgentMessage.agentName,
|
|
499
|
+
isComplete: false,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
];
|
|
503
|
+
}
|
|
504
|
+
return prev;
|
|
505
|
+
});
|
|
506
|
+
}, requestToolConfirmation, isToolAutoApproved, yoloMode);
|
|
341
507
|
// Check if aborted during tool execution
|
|
342
508
|
if (controller.signal.aborted) {
|
|
343
509
|
freeEncoder();
|
|
@@ -355,10 +521,26 @@ export async function handleConversationWithTools(options) {
|
|
|
355
521
|
}
|
|
356
522
|
}
|
|
357
523
|
}
|
|
524
|
+
// Remove only streaming sub-agent content messages (not tool-related messages)
|
|
525
|
+
// Keep sub-agent tool call and tool result messages for display
|
|
526
|
+
setMessages(prev => prev.filter(m => m.role !== 'subagent' ||
|
|
527
|
+
m.toolCall !== undefined ||
|
|
528
|
+
m.toolResult !== undefined ||
|
|
529
|
+
m.subAgentInternal === true));
|
|
358
530
|
// Update existing tool call messages with results
|
|
359
531
|
for (const result of toolResults) {
|
|
360
532
|
const toolCall = receivedToolCalls.find(tc => tc.id === result.tool_call_id);
|
|
361
533
|
if (toolCall) {
|
|
534
|
+
// Skip displaying result for sub-agent tools here
|
|
535
|
+
// Sub-agent results will be added by the callback after internal messages
|
|
536
|
+
if (toolCall.function.name.startsWith('subagent-')) {
|
|
537
|
+
// Still save the tool result to conversation history
|
|
538
|
+
conversationMessages.push(result);
|
|
539
|
+
saveMessage(result).catch(error => {
|
|
540
|
+
console.error('Failed to save tool result:', error);
|
|
541
|
+
});
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
362
544
|
const isError = result.content.startsWith('Error:');
|
|
363
545
|
const statusIcon = isError ? '✗' : '✓';
|
|
364
546
|
const statusText = isError ? `\n └─ ${result.content}` : '';
|
|
@@ -384,63 +566,34 @@ export async function handleConversationWithTools(options) {
|
|
|
384
566
|
// If parsing fails, just show regular result
|
|
385
567
|
}
|
|
386
568
|
}
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (msg.toolCallId === toolCall.id && msg.toolPending) {
|
|
415
|
-
return {
|
|
416
|
-
...msg,
|
|
417
|
-
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
418
|
-
toolPending: false,
|
|
419
|
-
toolCall: editDiffData
|
|
420
|
-
? {
|
|
421
|
-
name: toolCall.function.name,
|
|
422
|
-
arguments: editDiffData,
|
|
423
|
-
}
|
|
424
|
-
: terminalResultData
|
|
425
|
-
? {
|
|
426
|
-
name: toolCall.function.name,
|
|
427
|
-
arguments: terminalResultData,
|
|
428
|
-
}
|
|
429
|
-
: shouldShowPreview
|
|
430
|
-
? undefined // Clear toolCall for preview-enabled tools
|
|
431
|
-
: msg.toolCall, // Keep original toolCall for other tools
|
|
432
|
-
// Store tool result for preview rendering
|
|
433
|
-
toolResult: !isError ? result.content : undefined,
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
return msg;
|
|
437
|
-
}));
|
|
569
|
+
// Append completed message to static area (don't remove pending message)
|
|
570
|
+
// Static area doesn't support deletion, only append
|
|
571
|
+
// Completed message only shows diff data (for edit tools), no other parameters
|
|
572
|
+
setMessages(prev => [
|
|
573
|
+
...prev,
|
|
574
|
+
// Add new completed message
|
|
575
|
+
{
|
|
576
|
+
role: 'assistant',
|
|
577
|
+
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
578
|
+
streaming: false,
|
|
579
|
+
toolCall: editDiffData
|
|
580
|
+
? {
|
|
581
|
+
name: toolCall.function.name,
|
|
582
|
+
arguments: editDiffData,
|
|
583
|
+
}
|
|
584
|
+
: undefined, // Don't show arguments for completed tools (already shown in pending)
|
|
585
|
+
// Store tool result for preview rendering
|
|
586
|
+
toolResult: !isError ? result.content : undefined,
|
|
587
|
+
},
|
|
588
|
+
]);
|
|
589
|
+
}
|
|
590
|
+
// Add tool result to conversation history and save (skip if already saved above)
|
|
591
|
+
if (toolCall && !toolCall.function.name.startsWith('subagent-')) {
|
|
592
|
+
conversationMessages.push(result);
|
|
593
|
+
saveMessage(result).catch(error => {
|
|
594
|
+
console.error('Failed to save tool result:', error);
|
|
595
|
+
});
|
|
438
596
|
}
|
|
439
|
-
// Add tool result to conversation history and save
|
|
440
|
-
conversationMessages.push(result);
|
|
441
|
-
saveMessage(result).catch(error => {
|
|
442
|
-
console.error('Failed to save tool result:', error);
|
|
443
|
-
});
|
|
444
597
|
}
|
|
445
598
|
// After all tool results are processed, show TODO panel if there were todo-update calls
|
|
446
599
|
if (hasTodoUpdateTools) {
|
|
@@ -4,9 +4,11 @@ export declare function useSnapshotState(messagesLength: number): {
|
|
|
4
4
|
pendingRollback: {
|
|
5
5
|
messageIndex: number;
|
|
6
6
|
fileCount: number;
|
|
7
|
+
filePaths?: string[];
|
|
7
8
|
} | null;
|
|
8
9
|
setPendingRollback: import("react").Dispatch<import("react").SetStateAction<{
|
|
9
10
|
messageIndex: number;
|
|
10
11
|
fileCount: number;
|
|
12
|
+
filePaths?: string[];
|
|
11
13
|
} | null>>;
|
|
12
14
|
};
|
|
@@ -25,7 +25,7 @@ export function useToolConfirmation() {
|
|
|
25
25
|
* Check if a tool is auto-approved
|
|
26
26
|
*/
|
|
27
27
|
const isToolAutoApproved = (toolName) => {
|
|
28
|
-
return alwaysApprovedTools.has(toolName) || toolName.startsWith('todo-');
|
|
28
|
+
return alwaysApprovedTools.has(toolName) || toolName.startsWith('todo-') || toolName.startsWith('subagent-');
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
31
|
* Add a tool to the always-approved list
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SubAgentMessage } from '../utils/subAgentExecutor.js';
|
|
2
|
+
import type { ToolCall } from '../utils/toolExecutor.js';
|
|
3
|
+
export interface SubAgentToolExecutionOptions {
|
|
4
|
+
agentId: string;
|
|
5
|
+
prompt: string;
|
|
6
|
+
onMessage?: (message: SubAgentMessage) => void;
|
|
7
|
+
abortSignal?: AbortSignal;
|
|
8
|
+
requestToolConfirmation?: (toolCall: ToolCall, batchToolNames?: string, allTools?: ToolCall[]) => Promise<string>;
|
|
9
|
+
isToolAutoApproved?: (toolName: string) => boolean;
|
|
10
|
+
yoloMode?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Sub-Agent MCP Service
|
|
14
|
+
* Provides tools for executing sub-agents with their own specialized system prompts and tool access
|
|
15
|
+
*/
|
|
16
|
+
export declare class SubAgentService {
|
|
17
|
+
/**
|
|
18
|
+
* Execute a sub-agent as a tool
|
|
19
|
+
*/
|
|
20
|
+
execute(options: SubAgentToolExecutionOptions): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Get all available sub-agents as MCP tools
|
|
23
|
+
*/
|
|
24
|
+
getTools(): Array<{
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
inputSchema: any;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
export declare const subAgentService: SubAgentService;
|
|
31
|
+
export declare function getMCPTools(): Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
inputSchema: any;
|
|
35
|
+
}>;
|