@@ -103,6 +103,8 @@ export interface SessionState {
103103 sessionMcpServers ?: Array < string > ;
104104 terminalOutputMode : TerminalOutputMode ;
105105 currentGoal ?: ThreadGoalSnapshot | null ;
106+ sessionTitle : string | null ;
107+ sessionTitleSource : "unset" | "fallback" | "explicit" | "unknown" ;
106108}
107109
108110interface ActiveAuthState {
@@ -417,6 +419,8 @@ export class CodexAcpServer {
417419 currentModelSupportsFast : currentModelSupportsFast ,
418420 sessionMcpServers : sessionMcpServers ,
419421 terminalOutputMode : this . terminalOutputMode ,
422+ sessionTitle : null ,
423+ sessionTitleSource : "sessionId" in request ? "unknown" : "unset" ,
420424 } ;
421425 this . sessions . set ( sessionId , sessionState ) ;
422426 resumeSubscribed = false ;
@@ -946,6 +950,8 @@ export class CodexAcpServer {
946950 currentModelSupportsFast : currentModelSupportsFast ,
947951 sessionMcpServers : sessionMcpServers ,
948952 terminalOutputMode : this . terminalOutputMode ,
953+ sessionTitle : null ,
954+ sessionTitleSource : "unset" ,
949955 } ;
950956 this . sessions . set ( sessionId , sessionState ) ;
951957 subscribed = false ;
@@ -973,6 +979,7 @@ export class CodexAcpServer {
973979 private async streamThreadHistory ( sessionId : string , thread : Thread ) : Promise < void > {
974980 const session = new ACPSessionConnection ( this . connection , sessionId ) ;
975981 const sessionState = this . getSessionState ( sessionId ) ;
982+ await this . publishThreadHistoryTitle ( session , sessionState , thread ) ;
976983 const responseItemFallbackUpdates = await createResponseItemHistoryFallbackUpdates (
977984 thread ,
978985 sessionState . terminalOutputMode ,
@@ -994,6 +1001,67 @@ export class CodexAcpServer {
9941001 }
9951002 }
9961003
1004+ private async publishThreadHistoryTitle (
1005+ session : ACPSessionConnection ,
1006+ sessionState : SessionState ,
1007+ thread : Thread ,
1008+ ) : Promise < void > {
1009+ const explicitTitle = this . normalizeSessionTitle ( thread . name ) ;
1010+ if ( explicitTitle ) {
1011+ sessionState . sessionTitle = explicitTitle ;
1012+ sessionState . sessionTitleSource = "explicit" ;
1013+ await session . update ( {
1014+ sessionUpdate : "session_info_update" ,
1015+ title : explicitTitle ,
1016+ } ) ;
1017+ return ;
1018+ }
1019+
1020+ const historyTitle = this . findFirstUserMessageTitle ( thread )
1021+ ?? this . normalizeSessionTitle ( thread . preview ) ;
1022+ await this . publishFallbackSessionTitle ( sessionState , historyTitle ) ;
1023+ }
1024+
1025+ private findFirstUserMessageTitle ( thread : Thread ) : string | null {
1026+ for ( const turn of thread . turns ) {
1027+ for ( const item of turn . items ) {
1028+ if ( item . type !== "userMessage" ) continue ;
1029+ const title = this . normalizeSessionTitle ( item . content
1030+ . filter ( ( input ) : input is Extract < UserInput , { type : "text" } > => input . type === "text" )
1031+ . map ( input => input . text )
1032+ . join ( " " ) ) ;
1033+ if ( title ) return title ;
1034+ }
1035+ }
1036+ return null ;
1037+ }
1038+
1039+ private async publishFallbackSessionTitle (
1040+ sessionState : SessionState ,
1041+ title : string | null ,
1042+ ) : Promise < void > {
1043+ if ( sessionState . sessionTitleSource !== "unset" || ! title ) return ;
1044+ sessionState . sessionTitle = title ;
1045+ sessionState . sessionTitleSource = "fallback" ;
1046+ const session = new ACPSessionConnection ( this . connection , sessionState . sessionId ) ;
1047+ await session . update ( {
1048+ sessionUpdate : "session_info_update" ,
1049+ title,
1050+ } ) ;
1051+ }
1052+
1053+ private createPromptFallbackTitle ( prompt : acp . ContentBlock [ ] ) : string | null {
1054+ return this . normalizeSessionTitle ( prompt
1055+ . filter ( ( block ) : block is Extract < acp . ContentBlock , { type : "text" } > => block . type === "text" )
1056+ . map ( block => block . text )
1057+ . join ( " " ) ) ;
1058+ }
1059+
1060+ private normalizeSessionTitle ( title : string | null | undefined ) : string | null {
1061+ const normalized = title ?. replace ( / \s + / g, " " ) . trim ( ) ?? "" ;
1062+ return normalized . length > 0 ? normalized : null ;
1063+ }
1064+
9971065 private async createHistoryUpdates ( item : ThreadItem , sessionState : SessionState ) : Promise < UpdateSessionEvent [ ] > {
9981066 switch ( item . type ) {
9991067 case "userMessage" :
@@ -1588,6 +1656,11 @@ export class CodexAcpServer {
15881656 throw error ;
15891657 }
15901658
1659+ await this . publishFallbackSessionTitle (
1660+ sessionState ,
1661+ this . createPromptFallbackTitle ( params . prompt ) ,
1662+ ) ;
1663+
15911664 return {
15921665 stopReason : "end_turn" ,
15931666 usage : this . buildPromptUsage ( sessionState . lastTokenUsage ) ,
0 commit comments