snow-ai 0.3.4 → 0.3.6
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/types.d.ts +1 -0
- package/dist/hooks/useConversation.js +226 -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/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/components/ToolResultPreview.js +21 -1
- package/dist/ui/pages/ChatScreen.js +29 -20
- package/dist/ui/pages/ConfigScreen.js +47 -46
- 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/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 {
|
package/dist/api/types.d.ts
CHANGED
|
@@ -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,40 @@ 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
|
+
// Special handling for sub-agent tools - show completion message
|
|
535
|
+
// Pass the full JSON result to ToolResultPreview for proper parsing
|
|
536
|
+
if (toolCall.function.name.startsWith('subagent-')) {
|
|
537
|
+
const isError = result.content.startsWith('Error:');
|
|
538
|
+
const statusIcon = isError ? '✗' : '✓';
|
|
539
|
+
const statusText = isError ? `\n └─ ${result.content}` : '';
|
|
540
|
+
// Display subagent completion message in main flow
|
|
541
|
+
setMessages(prev => [
|
|
542
|
+
...prev,
|
|
543
|
+
{
|
|
544
|
+
role: 'assistant',
|
|
545
|
+
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
546
|
+
streaming: false,
|
|
547
|
+
// Pass the full result.content for ToolResultPreview to parse
|
|
548
|
+
toolResult: !isError ? result.content : undefined,
|
|
549
|
+
},
|
|
550
|
+
]);
|
|
551
|
+
// Save the tool result to conversation history
|
|
552
|
+
conversationMessages.push(result);
|
|
553
|
+
saveMessage(result).catch(error => {
|
|
554
|
+
console.error('Failed to save tool result:', error);
|
|
555
|
+
});
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
362
558
|
const isError = result.content.startsWith('Error:');
|
|
363
559
|
const statusIcon = isError ? '✗' : '✓';
|
|
364
560
|
const statusText = isError ? `\n └─ ${result.content}` : '';
|
|
@@ -384,63 +580,34 @@ export async function handleConversationWithTools(options) {
|
|
|
384
580
|
// If parsing fails, just show regular result
|
|
385
581
|
}
|
|
386
582
|
}
|
|
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
|
-
}));
|
|
583
|
+
// Append completed message to static area (don't remove pending message)
|
|
584
|
+
// Static area doesn't support deletion, only append
|
|
585
|
+
// Completed message only shows diff data (for edit tools), no other parameters
|
|
586
|
+
setMessages(prev => [
|
|
587
|
+
...prev,
|
|
588
|
+
// Add new completed message
|
|
589
|
+
{
|
|
590
|
+
role: 'assistant',
|
|
591
|
+
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
592
|
+
streaming: false,
|
|
593
|
+
toolCall: editDiffData
|
|
594
|
+
? {
|
|
595
|
+
name: toolCall.function.name,
|
|
596
|
+
arguments: editDiffData,
|
|
597
|
+
}
|
|
598
|
+
: undefined, // Don't show arguments for completed tools (already shown in pending)
|
|
599
|
+
// Store tool result for preview rendering
|
|
600
|
+
toolResult: !isError ? result.content : undefined,
|
|
601
|
+
},
|
|
602
|
+
]);
|
|
603
|
+
}
|
|
604
|
+
// Add tool result to conversation history and save (skip if already saved above)
|
|
605
|
+
if (toolCall && !toolCall.function.name.startsWith('subagent-')) {
|
|
606
|
+
conversationMessages.push(result);
|
|
607
|
+
saveMessage(result).catch(error => {
|
|
608
|
+
console.error('Failed to save tool result:', error);
|
|
609
|
+
});
|
|
438
610
|
}
|
|
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
611
|
}
|
|
445
612
|
// After all tool results are processed, show TODO panel if there were todo-update calls
|
|
446
613
|
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
|
+
}>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { executeSubAgent } from '../utils/subAgentExecutor.js';
|
|
2
|
+
import { getSubAgents } from '../utils/subAgentConfig.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sub-Agent MCP Service
|
|
5
|
+
* Provides tools for executing sub-agents with their own specialized system prompts and tool access
|
|
6
|
+
*/
|
|
7
|
+
export class SubAgentService {
|
|
8
|
+
/**
|
|
9
|
+
* Execute a sub-agent as a tool
|
|
10
|
+
*/
|
|
11
|
+
async execute(options) {
|
|
12
|
+
const { agentId, prompt, onMessage, abortSignal, requestToolConfirmation, isToolAutoApproved, yoloMode, } = options;
|
|
13
|
+
// Create a tool confirmation adapter for sub-agent if needed
|
|
14
|
+
const subAgentToolConfirmation = requestToolConfirmation
|
|
15
|
+
? async (toolName, toolArgs) => {
|
|
16
|
+
// Create a fake tool call for confirmation
|
|
17
|
+
const fakeToolCall = {
|
|
18
|
+
id: 'subagent-tool',
|
|
19
|
+
type: 'function',
|
|
20
|
+
function: {
|
|
21
|
+
name: toolName,
|
|
22
|
+
arguments: JSON.stringify(toolArgs),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
return await requestToolConfirmation(fakeToolCall);
|
|
26
|
+
}
|
|
27
|
+
: undefined;
|
|
28
|
+
const result = await executeSubAgent(agentId, prompt, onMessage, abortSignal, subAgentToolConfirmation, isToolAutoApproved, yoloMode);
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
throw new Error(result.error || 'Sub-agent execution failed');
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
result: result.result,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get all available sub-agents as MCP tools
|
|
39
|
+
*/
|
|
40
|
+
getTools() {
|
|
41
|
+
const subAgents = getSubAgents();
|
|
42
|
+
return subAgents.map(agent => ({
|
|
43
|
+
name: agent.id,
|
|
44
|
+
description: `${agent.name}: ${agent.description}`,
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
prompt: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'The task prompt to send to the sub-agent',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ['prompt'],
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Export a default instance
|
|
59
|
+
export const subAgentService = new SubAgentService();
|
|
60
|
+
// MCP Tool definitions (dynamically generated from configuration)
|
|
61
|
+
// Note: These are generated at runtime, so we export a function instead of a constant
|
|
62
|
+
export function getMCPTools() {
|
|
63
|
+
return subAgentService.getTools();
|
|
64
|
+
}
|
|
@@ -20,7 +20,6 @@ type Props = {
|
|
|
20
20
|
cacheReadTokens?: number;
|
|
21
21
|
cachedTokens?: number;
|
|
22
22
|
};
|
|
23
|
-
snapshotFileCount?: Map<number, number>;
|
|
24
23
|
};
|
|
25
|
-
export default function ChatInput({ onSubmit, onCommand, placeholder, disabled, chatHistory, onHistorySelect, yoloMode, contextUsage,
|
|
24
|
+
export default function ChatInput({ onSubmit, onCommand, placeholder, disabled, chatHistory, onHistorySelect, yoloMode, contextUsage, }: Props): React.JSX.Element;
|
|
26
25
|
export {};
|