@@ -588,10 +588,6 @@ class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<
588588
589589 getChildren ( element ?: TreeItem | undefined ) : vscode . ProviderResult < TreeItem [ ] > {
590590 if ( element instanceof ChatPromptItem ) {
591- // If mainEntryId is set, filter out the main entry from children
592- if ( element . mainEntryId ) {
593- return element . children . filter ( child => child . id !== element . mainEntryId ) ;
594- }
595591 return element . children ;
596592 } else if ( element ) {
597593 return [ ] ;
@@ -600,17 +596,15 @@ class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<
600596 const tokenToPrompt = new Map < CapturingToken , ChatPromptItem > ( ) ;
601597
602598 for ( const currReq of this . requestLogger . getRequests ( ) ) {
603- // Skip entries that are dynamically hidden (e.g. skipped/cancelled live NES requests)
604- if ( currReq . kind === LoggedInfoKind . Request &&
605- currReq . entry . type === LoggedRequestKind . MarkdownContentRequest &&
606- currReq . entry . isVisible && ! currReq . entry . isVisible ( ) ) {
607- continue ;
608- }
609-
610- const currReqTreeItem = this . logToTreeItem ( currReq ) ;
611-
612599 if ( ! currReq . token ) {
613- result . push ( currReqTreeItem ) ;
600+ // Skip non-main hidden entries (e.g. skipped/cancelled live NES requests)
601+ if ( currReq . kind === LoggedInfoKind . Request &&
602+ currReq . entry . type === LoggedRequestKind . MarkdownContentRequest &&
603+ currReq . entry . isVisible && ! currReq . entry . isVisible ( ) ) {
604+ continue ;
605+ }
606+
607+ result . push ( this . logToTreeItem ( currReq ) ) ;
614608 continue ;
615609 }
616610
@@ -621,30 +615,38 @@ class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<
621615 result . push ( prompt ) ;
622616 }
623617
618+ // If this entry is the main entry for the group (a MarkdownContentRequest
619+ // whose debugName matches the token label), associate it directly with the
620+ // parent ChatPromptItem — don't add it as a child. The entry stays in the
621+ // request logger for virtual document serving; only tree nesting changes.
622+ // Only wire the main entry if it is visible — for live NES/Ghost entries,
623+ // isVisible() can be false (e.g. skipped/cancelled); wiring a hidden entry
624+ // would make it visible again via the parent's icon and click command.
625+ if ( currReq . kind === LoggedInfoKind . Request &&
626+ currReq . entry . type === LoggedRequestKind . MarkdownContentRequest &&
627+ currReq . entry . debugName === currReq . token . label ) {
628+ const isHidden = currReq . entry . isVisible && ! currReq . entry . isVisible ( ) ;
629+ if ( ! isHidden ) {
630+ prompt . setMainEntry ( currReq ) ;
631+ }
632+ continue ;
633+ }
634+
635+ // Skip non-main hidden entries
636+ if ( currReq . kind === LoggedInfoKind . Request &&
637+ currReq . entry . type === LoggedRequestKind . MarkdownContentRequest &&
638+ currReq . entry . isVisible && ! currReq . entry . isVisible ( ) ) {
639+ continue ;
640+ }
641+
642+ const currReqTreeItem = this . logToTreeItem ( currReq ) ;
624643 const alreadyIncluded = prompt . children . find ( existingChild => existingChild . id === currReqTreeItem . id ) ;
625644 if ( ! alreadyIncluded ) {
626645 prompt . children . push ( currReqTreeItem ) ;
627646 }
628647 }
629648
630- // Post-process: flatten single-child items and promote main entries
631- const processed : ( ChatPromptItem | TreeChildItem ) [ ] = [ ] ;
632- for ( const item of result ) {
633- if ( item instanceof ChatPromptItem ) {
634- if ( item . token . flattenSingleChild && item . children . length === 1 ) {
635- processed . push ( item . children [ 0 ] ) ;
636- } else {
637- if ( item . token . promoteMainEntry ) {
638- item . promoteMainEntry ( ) ;
639- }
640- processed . push ( item ) ;
641- }
642- } else {
643- processed . push ( item ) ;
644- }
645- }
646-
647- return filterMap ( processed , r => {
649+ return filterMap ( result , r => {
648650 if ( ! this . filters . itemIncluded ( r ) ) {
649651 return undefined ;
650652 }
@@ -679,72 +681,59 @@ class ChatPromptItem extends vscode.TreeItem {
679681 override readonly contextValue = 'chatprompt' ;
680682 public children : TreeChildItem [ ] = [ ] ;
681683 public override id : string | undefined ;
682- /**
683- * The ID of the main entry that was promoted to this parent item.
684- * When set, clicking the parent opens this entry and it's excluded from children.
685- */
686- public mainEntryId : string | undefined ;
687684
688685 public static create ( info : LoggedInfo , request : CapturingToken ) {
689686 const existing = ChatPromptItem . ids . get ( info ) ;
690687 if ( existing ) {
691688 return existing ;
692689 }
693690
694- // Check if this first info should be promoted to the main entry
695- let mainEntryId : string | undefined ;
696- if ( request . promoteMainEntry && info . kind === LoggedInfoKind . Request ) {
697- const requestInfo = info as ILoggedRequestInfo ;
698- if ( requestInfo . entry . debugName === request . label ) {
699- mainEntryId = info . id ;
700- }
701- }
702-
703- const item = new ChatPromptItem ( request , mainEntryId ) ;
691+ const item = new ChatPromptItem ( request ) ;
704692 item . id = info . id + '-prompt' ;
705693 ChatPromptItem . ids . set ( info , item ) ;
706694 return item ;
707695 }
708696
709- protected constructor ( public readonly token : CapturingToken , mainEntryId ?: string ) {
697+ protected constructor ( public readonly token : CapturingToken ) {
710698 super ( token . label , vscode . TreeItemCollapsibleState . Expanded ) ;
711699 if ( token . icon ) {
712700 this . iconPath = new vscode . ThemeIcon ( token . icon ) ;
713701 }
714- if ( mainEntryId ) {
715- this . mainEntryId = mainEntryId ;
716- this . command = {
717- command : 'vscode.open' ,
718- title : '' ,
719- arguments : [ vscode . Uri . parse ( ChatRequestScheme . buildUri ( { kind : 'request' , id : mainEntryId } ) ) ]
720- } ;
721- }
722702 }
723703
724- public promoteMainEntry ( ) {
725- const child = this . children . find ( child => child . label === this . label ) ;
726- if ( ! child || child . id === undefined ) {
704+ /**
705+ * Associate a main entry directly with this parent item.
706+ * The main entry's icon and click command are shown on the parent node.
707+ * The entry is NOT added as a child — it stays in the request logger
708+ * for virtual document serving only.
709+ */
710+ public setMainEntry ( info : ILoggedRequestInfo ) : void {
711+ if ( info . entry . type !== LoggedRequestKind . MarkdownContentRequest ) {
727712 return ;
728713 }
729- this . mainEntryId = child . id ;
730- if ( child . iconPath ) {
731- this . iconPath = child . iconPath ;
714+ const resolvedIcon = resolveMarkdownIcon ( info . entry ) ;
715+ if ( resolvedIcon !== undefined ) {
716+ this . iconPath = new vscode . ThemeIcon ( resolvedIcon . id ) ;
732717 }
733718 this . command = {
734719 command : 'vscode.open' ,
735720 title : '' ,
736- arguments : [ vscode . Uri . parse ( ChatRequestScheme . buildUri ( { kind : 'request' , id : this . mainEntryId } ) ) ]
721+ arguments : [ vscode . Uri . parse ( ChatRequestScheme . buildUri ( { kind : 'request' , id : info . id } ) ) ]
737722 } ;
738723 }
739724
740725 public withFilteredChildren ( filter : ( child : TreeChildItem ) => boolean ) : ChatPromptItem {
741- const item = new ChatPromptItem ( this . token , this . mainEntryId ) ;
726+ const item = new ChatPromptItem ( this . token ) ;
742727 item . children = this . children . filter ( filter ) ;
743728 item . id = this . id ;
744729 item . iconPath = this . iconPath ;
745730 item . command = this . command ;
731+ item . collapsibleState = item . children . length > 0
732+ ? vscode . TreeItemCollapsibleState . Expanded
733+ : vscode . TreeItemCollapsibleState . None ;
746734 return item ;
747735 }
736+
748737}
749738
750739class ToolCallItem extends vscode . TreeItem {
0 commit comments