Skip to content

Commit 101249f

Browse files
committed
fix mcp tool display
1 parent 7497272 commit 101249f

File tree

8 files changed

+122
-169
lines changed

8 files changed

+122
-169
lines changed

config/gni/devtools_grd_files.gni

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -866,17 +866,7 @@ grd_files_bundled_sources = [
866866
"front_end/third_party/lighthouse/lighthouse-dt-bundle.js",
867867
"front_end/third_party/lighthouse/report/report.js",
868868
"front_end/third_party/lit/lit.js",
869-
"front_end/third_party/mcp-sdk/ajv/dist/ajv.js",
870-
"front_end/third_party/mcp-sdk/zod/lib/index.js",
871-
"front_end/third_party/mcp-sdk/zod/lib/index.mjs",
872-
"front_end/third_party/mcp-sdk/eventsource-parser/package/dist/index.js",
873-
"front_end/third_party/mcp-sdk/eventsource-parser/package/dist/stream.js",
874869
"front_end/third_party/mcp-sdk/mcp-sdk.js",
875-
"front_end/third_party/mcp-sdk/package/dist/client/index.js",
876-
"front_end/third_party/mcp-sdk/package/dist/client/sse.js",
877-
"front_end/third_party/mcp-sdk/package/dist/shared/protocol.js",
878-
"front_end/third_party/mcp-sdk/package/dist/shared/transport.js",
879-
"front_end/third_party/mcp-sdk/package/dist/types.js",
880870
"front_end/third_party/marked/marked.js",
881871
"front_end/third_party/puppeteer-replay/puppeteer-replay.js",
882872
"front_end/third_party/puppeteer/puppeteer.js",
@@ -1389,6 +1379,7 @@ grd_files_unbundled_sources = [
13891379
"front_end/panels/ai_assistance/components/ChatView.js",
13901380
"front_end/panels/ai_assistance/components/ExploreWidget.js",
13911381
"front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.js",
1382+
"front_end/panels/ai_assistance/components/ScrollPinHelper.js",
13921383
"front_end/panels/ai_assistance/components/UserActionRow.js",
13931384
"front_end/panels/ai_assistance/components/chatView.css.js",
13941385
"front_end/panels/ai_assistance/components/exploreWidget.css.js",

front_end/panels/ai_chat/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ _ai_chat_sources = [
207207
"LLM/MessageSanitizer.ts",
208208
"LLM/LLMClient.ts",
209209
"tools/Tools.ts",
210+
"tools/LLMTracingWrapper.ts",
210211
"tools/CritiqueTool.ts",
211212
"tools/FetcherTool.ts",
212213
"tools/FinalizeWithCritiqueTool.ts",

front_end/panels/ai_chat/core/AgentNodes.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper
257257
tracingContext.currentToolCallId = toolCallObservationId;
258258
}
259259

260+
// Determine lane: agent tools render in agent lane only
261+
const regTool = ToolRegistry.getRegisteredTool(parsedAction.name as any);
262+
const isAgentTool = !!regTool && (regTool instanceof ConfigurableAgentTool);
263+
260264
newModelMessage = {
261265
entity: ChatMessageEntity.MODEL,
262266
action: 'tool',
@@ -265,6 +269,7 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper
265269
toolCallId, // Add for linking with tool response
266270
isFinalAnswer: false,
267271
reasoning: response.reasoning?.summary,
272+
uiLane: isAgentTool ? 'agent' : 'chat',
268273
};
269274

270275
logger.debug('AgentNode: Created tool message', { toolName: parsedAction.name, toolCallId });
@@ -732,15 +737,15 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider,
732737
}
733738

734739
// Create the NEW ToolResultMessage
740+
const isAgentTool = selectedTool instanceof ConfigurableAgentTool;
735741
const toolResultMessage: ToolResultMessage = {
736742
entity: ChatMessageEntity.TOOL_RESULT,
737743
toolName,
738744
resultText,
739745
isError,
740746
toolCallId, // Link back to the tool call for OpenAI format
741747
...(isError && { error: resultText }),
742-
// Mark if this is from a ConfigurableAgentTool
743-
...(selectedTool instanceof ConfigurableAgentTool && { isFromConfigurableAgent: true })
748+
uiLane: isAgentTool ? 'agent' as const : 'chat',
744749
};
745750

746751
logger.debug('ToolExecutorNode: Adding tool result message with toolCallId:', { toolCallId, toolResultMessage });

front_end/panels/ai_chat/core/ToolNameMapping.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { createToolExecutorNode } from './AgentNodes.js';
66
import type { AgentState } from './State.js';
77
import type { Tool } from '../tools/Tools.js';
8-
import { ChatMessageEntity } from '../ui/ChatView.js';
8+
import { ChatMessageEntity } from '../models/ChatTypes.js';
99

1010
/* eslint-env mocha */
1111

@@ -66,7 +66,7 @@ describe('AgentNodes sanitized tool name mapping', () => {
6666
}
6767
} as any;
6868

69-
const node = createToolExecutorNode(state);
69+
const node = createToolExecutorNode(state, 'openai', 'gpt-4', 'gpt-4-mini', 'gpt-4-mini');
7070
const result = await node.invoke(state);
7171

7272
// Tool execute should have been called once

front_end/panels/ai_chat/models/ChatTypes.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@ export enum ChatMessageEntity {
1313
}
1414

1515
// Base structure for all chat messages
16+
export type UILane = 'chat' | 'agent';
17+
1618
export interface BaseChatMessage {
1719
entity: ChatMessageEntity;
1820
error?: string;
21+
// UI routing hint: which lane should render this message
22+
uiLane?: UILane;
23+
// If managed by an AgentSession, provide the session id for traceability
24+
managedByAgentSessionId?: string;
1925
}
2026

2127
// Image input used by user messages
@@ -74,4 +80,3 @@ export enum State {
7480
LOADING = 'loading',
7581
ERROR = 'error',
7682
}
77-

front_end/panels/ai_chat/ui/ChatView.ts

Lines changed: 96 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -157,36 +157,7 @@ export class ChatView extends HTMLElement {
157157
this.#structuredController.resetLastProcessed();
158158
}
159159

160-
/**
161-
* Check if a message is part of an agent session
162-
*/
163-
#isPartOfAgentSession(message: ChatMessage): boolean {
164-
// Check if there's an AgentSessionMessage in the current messages
165-
const hasAgentSession = this.#messages.some(msg => msg.entity === ChatMessageEntity.AGENT_SESSION);
166-
167-
if (!hasAgentSession) {
168-
return false;
169-
}
170-
171-
// For ModelChatMessage tool calls, check if they're from ConfigurableAgentTool
172-
if (message.entity === ChatMessageEntity.MODEL) {
173-
const modelMsg = message as ModelChatMessage;
174-
if (modelMsg.action === 'tool' && modelMsg.toolName) {
175-
// Check if there's a corresponding tool result that's from ConfigurableAgentTool
176-
const toolResultIndex = this.#messages.findIndex((msg) =>
177-
msg.entity === ChatMessageEntity.TOOL_RESULT &&
178-
(msg as ToolResultMessage).toolName === modelMsg.toolName &&
179-
(msg as ToolResultMessage).toolCallId === modelMsg.toolCallId
180-
);
181-
if (toolResultIndex !== -1) {
182-
const toolResult = this.#messages[toolResultIndex] as ToolResultMessage;
183-
return toolResult.isFromConfigurableAgent === true;
184-
}
185-
}
186-
}
187-
188-
return false;
189-
}
160+
// Lane-based routing: deprecated session-heuristics removed
190161

191162
// Scroll behavior handled by <ai-message-list>
192163

@@ -399,6 +370,10 @@ export class ChatView extends HTMLElement {
399370
// Render messages based on the combined structure
400371
#renderMessage(message: ChatMessage | (ModelChatMessage & { resultText?: string, isError?: boolean, resultError?: string, combined?: boolean }) | (ToolResultMessage & { orphaned?: boolean }), combinedIndex?: number ): Lit.TemplateResult {
401372
try {
373+
// Lane filter: hide agent-lane items from the main chat feed
374+
if ((message as any).uiLane === 'agent') {
375+
return html``;
376+
}
402377
switch (message.entity) {
403378
case ChatMessageEntity.USER:
404379
// Render User Message via dedicated renderer
@@ -439,9 +414,7 @@ export class ChatView extends HTMLElement {
439414
case ChatMessageEntity.TOOL_RESULT:
440415
{
441416
const toolResultMessage = message as (ToolResultMessage & { orphaned?: boolean });
442-
if (toolResultMessage.isFromConfigurableAgent) {
443-
return html``;
444-
}
417+
// Lane-based filter already handled above
445418
if (toolResultMessage.orphaned) {
446419
return renderToolResultMessage(toolResultMessage);
447420
}
@@ -451,14 +424,7 @@ export class ChatView extends HTMLElement {
451424
{
452425
// Cast to the potentially combined type
453426
const modelMessage = message as (ModelChatMessage & { resultText?: string, isError?: boolean, resultError?: string, combined?: boolean });
454-
455-
// Hide tool calls that are part of agent sessions
456-
if (modelMessage.action === 'tool') {
457-
const isPartOfSession = this.#isPartOfAgentSession(modelMessage);
458-
if (isPartOfSession) {
459-
return html``;
460-
}
461-
}
427+
// Lane filter above already hides agent-managed tool calls
462428

463429
// Check if it's a combined message (tool call + result) or just a running tool call / final answer
464430
const isCombined = modelMessage.combined === true;
@@ -497,79 +463,78 @@ export class ChatView extends HTMLElement {
497463
const icon = ToolDescriptionFormatter.getToolIcon(toolName);
498464
const descriptionData = ToolDescriptionFormatter.getToolDescription(toolName, toolArgs);
499465

500-
return html``
501-
// return html`
502-
// <!-- Reasoning (if any) displayed above the timeline -->
503-
// ${toolReasoning ? html`
504-
// <div class="message-text reasoning-text" style="margin-bottom: 8px;">
505-
// ${renderMarkdown(toolReasoning, this.#markdownRenderer, this.#openInAIAssistantViewer.bind(this))}
506-
// </div>
507-
// ` : Lit.nothing}
508-
509-
// <!-- Timeline Tool Execution -->
510-
// <div class="agent-execution-timeline single-tool">
511-
// <!-- Tool Header -->
512-
// <div class="agent-header">
513-
// <div class="agent-marker"></div>
514-
// <div class="agent-title">${descriptionData.action}</div>
515-
// <div class="agent-divider"></div>
516-
// <button class="tool-toggle" @click=${(e: Event) => this.#toggleToolResult(e)}>
517-
// <span class="toggle-icon">▼</span>
518-
// </button>
519-
// </div>
466+
return html`
467+
<!-- Reasoning (if any) displayed above the timeline -->
468+
${toolReasoning ? html`
469+
<div class="message-text reasoning-text" style="margin-bottom: 8px;">
470+
${renderMarkdown(toolReasoning, this.#markdownRenderer)}
471+
</div>
472+
` : Lit.nothing}
473+
474+
<!-- Timeline Tool Execution -->
475+
<div class="agent-execution-timeline single-tool">
476+
<!-- Tool Header -->
477+
<div class="agent-header">
478+
<div class="agent-marker"></div>
479+
<div class="agent-title">${descriptionData.action}</div>
480+
<div class="agent-divider"></div>
481+
<button class="tool-toggle" @click=${(e: Event) => this.#toggleToolResult(e)}>
482+
<span class="toggle-icon"></span>
483+
</button>
484+
</div>
520485
521-
// <div class="timeline-items" style="display: none;">
522-
// <div class="timeline-item">
523-
// <div class="tool-line">
524-
// ${descriptionData.isMultiLine ? html`
525-
// <div class="tool-summary">
526-
// <span class="tool-description">
527-
// <span class="tool-description-indicator">└─</span>
528-
// <div>${(descriptionData.content as Array<{key: string, value: string}>)[0]?.value || 'multiple parameters'}</div>
529-
// </span>
530-
// <span class="tool-status-marker ${status}" title="${status === 'running' ? 'Running' : status === 'completed' ? 'Completed' : status === 'error' ? 'Error' : 'Unknown'}">●</span>
531-
// </div>
532-
// ` : html`
533-
// <span class="tool-description">
534-
// <span class="tool-description-indicator">└─</span>
535-
// <div>${descriptionData.content}</div>
536-
// </span>
537-
// <span class="tool-status-marker ${status}" title="${status === 'running' ? 'Running' : status === 'completed' ? 'Completed' : status === 'error' ? 'Error' : 'Unknown'}">●</span>
538-
// `}
539-
// </div>
486+
<div class="timeline-items" style="display: none;">
487+
<div class="timeline-item">
488+
<div class="tool-line">
489+
${descriptionData.isMultiLine ? html`
490+
<div class="tool-summary">
491+
<span class="tool-description">
492+
<span class="tool-description-indicator">└─</span>
493+
<div>${(descriptionData.content as Array<{key: string, value: string}>)[0]?.value || 'multiple parameters'}</div>
494+
</span>
495+
<span class="tool-status-marker ${status}" title="${status === 'running' ? 'Running' : status === 'completed' ? 'Completed' : status === 'error' ? 'Error' : 'Unknown'}"></span>
496+
</div>
497+
` : html`
498+
<span class="tool-description">
499+
<span class="tool-description-indicator">└─</span>
500+
<div>${descriptionData.content}</div>
501+
</span>
502+
<span class="tool-status-marker ${status}" title="${status === 'running' ? 'Running' : status === 'completed' ? 'Completed' : status === 'error' ? 'Error' : 'Unknown'}"></span>
503+
`}
504+
</div>
540505
541-
// <!-- Result Block - Integrated within timeline item -->
542-
// ${isCombined && resultText ? html`
543-
// <div class="tool-result-integrated ${status}">
544-
// Response:
545-
// ${this.#formatJsonWithSyntaxHighlighting(resultText)}
546-
// </div>
547-
// ` : Lit.nothing}
548-
// </div>
549-
// </div>
506+
<!-- Result Block - Integrated within timeline item -->
507+
${isCombined && resultText ? html`
508+
<div class="tool-result-integrated ${status}">
509+
Response:
510+
${this.#formatJsonWithSyntaxHighlighting(resultText)}
511+
</div>
512+
` : Lit.nothing}
513+
</div>
514+
</div>
550515
551-
// <!-- Loading spinner for running tools -->
552-
// ${status === 'running' ? html`
553-
// <div class="tool-loading">
554-
// <svg class="loading-spinner" width="16" height="16" viewBox="0 0 16 16">
555-
// <circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none" stroke-dasharray="30 12" stroke-linecap="round">
556-
// <animateTransform
557-
// attributeName="transform"
558-
// attributeType="XML"
559-
// type="rotate"
560-
// from="0 8 8"
561-
// to="360 8 8"
562-
// dur="1s"
563-
// repeatCount="indefinite" />
564-
// </circle>
565-
// </svg>
566-
// </div>
567-
// ` : Lit.nothing}
568-
569-
// <!-- Error messages -->
570-
// ${modelMessage.error ? html`<div class="message-error tool-error-message">Model Error: ${modelMessage.error}</div>` : Lit.nothing}
571-
// </div>
572-
// `;
516+
<!-- Loading spinner for running tools -->
517+
${status === 'running' ? html`
518+
<div class="tool-loading">
519+
<svg class="loading-spinner" width="16" height="16" viewBox="0 0 16 16">
520+
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none" stroke-dasharray="30 12" stroke-linecap="round">
521+
<animateTransform
522+
attributeName="transform"
523+
attributeType="XML"
524+
type="rotate"
525+
from="0 8 8"
526+
to="360 8 8"
527+
dur="1s"
528+
repeatCount="indefinite" />
529+
</circle>
530+
</svg>
531+
</div>
532+
` : Lit.nothing}
533+
534+
<!-- Error messages -->
535+
${modelMessage.error ? html`<div class="message-error tool-error-message">Model Error: ${modelMessage.error}</div>` : Lit.nothing}
536+
</div>
537+
`;
573538
}
574539
default:
575540
// Should not happen, but render a fallback
@@ -976,6 +941,26 @@ export class ChatView extends HTMLElement {
976941
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
977942
}
978943

944+
/**
945+
* Toggle visibility of tool result details
946+
*/
947+
#toggleToolResult(event: Event): void {
948+
const button = event.currentTarget as HTMLElement;
949+
const timeline = button.closest('.agent-execution-timeline');
950+
const items = timeline?.querySelector('.timeline-items') as HTMLElement;
951+
const icon = button.querySelector('.toggle-icon') as HTMLElement;
952+
953+
if (items) {
954+
if (items.style.display === 'none') {
955+
items.style.display = 'block';
956+
icon.textContent = '▲';
957+
} else {
958+
items.style.display = 'none';
959+
icon.textContent = '▼';
960+
}
961+
}
962+
}
963+
979964
// Stable key for message list rendering to avoid node reuse glitches
980965
#messageKey(m: CombinedMessage, index: number): string {
981966
// Agent sessions keyed by sessionId to ensure distinct component instances render

front_end/panels/ai_chat/ui/ToolCallComponent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class ToolCallComponent extends HTMLElement {
1818

1919
private toolCall: AgentMessage | null = null;
2020
private status: ToolStatus = 'running';
21-
private isExpanded = false;
21+
private isExpanded = true;
2222

2323
connectedCallback(): void {
2424
this.render();
@@ -243,4 +243,4 @@ export class ToolCallComponent extends HTMLElement {
243243
default: return 'Unknown';
244244
}
245245
}
246-
}
246+
}

0 commit comments

Comments
 (0)