11/* ==========================================================================
22 Chat Session Manager
3- Persists raw chat conversations as JournalEntry documents in a dedicated
4- " FoundryAI Chat History" folder .
3+ Persists raw chat conversations as JournalEntry documents in the
4+ FoundryAI/ Chat History subfolder (or FoundryAI/Actors for roleplay) .
55 ========================================================================== */
66
77import type { LLMMessage } from './openrouter-service'
8+ import { getSubfolderId } from './folder-manager'
89
910const MODULE_ID = 'foundry-ai'
10- const CHAT_HISTORY_FOLDER_NAME = 'FoundryAI Chat History'
1111
1212export interface ChatSession {
1313 id : string // JournalEntry document ID
@@ -17,6 +17,8 @@ export interface ChatSession {
1717 updatedAt : number // Epoch ms
1818 model : string // Model used for this session
1919 tokenCount ?: number // Approximate total tokens
20+ actorId ?: string // If this is an actor roleplay session
21+ actorName ?: string // Display name of the actor
2022}
2123
2224export interface SessionSummary {
@@ -26,6 +28,8 @@ export interface SessionSummary {
2628 updatedAt : number
2729 messageCount : number
2830 model : string
31+ actorId ?: string
32+ actorName ?: string
2933}
3034
3135class ChatSessionManager {
@@ -40,22 +44,31 @@ class ChatSessionManager {
4044 if ( folder ) return folder
4145 }
4246
43- // Find existing folder
47+ // Use the FoundryAI/Chat History subfolder
48+ const subfolderId = getSubfolderId ( 'chatHistory' )
49+ if ( subfolderId ) {
50+ const folder = game . folders ?. get ( subfolderId )
51+ if ( folder ) return folder
52+ }
53+
54+ // Find existing folder (legacy fallback)
4455 if ( this . folderCache ) {
4556 const cached = game . folders ?. get ( this . folderCache )
4657 if ( cached ) return cached
4758 }
4859
49- const existing = game . folders ?. find ( ( f : Folder ) => f . name === CHAT_HISTORY_FOLDER_NAME && f . type === 'JournalEntry' )
60+ const existing = game . folders ?. find (
61+ ( f : Folder ) => f . name === 'Chat History' && f . type === 'JournalEntry' ,
62+ )
5063
5164 if ( existing ) {
5265 this . folderCache = existing . id
5366 return existing
5467 }
5568
56- // Create folder
69+ // Create folder as last resort
5770 const folder = await Folder . create ( {
58- name : CHAT_HISTORY_FOLDER_NAME ,
71+ name : 'Chat History' ,
5972 type : 'JournalEntry' ,
6073 color : '#5e4fa2' ,
6174 } )
@@ -64,30 +77,39 @@ class ChatSessionManager {
6477 return folder
6578 }
6679
80+ /** Get the Actors roleplay subfolder */
81+ getActorRoleplayFolder ( ) : any | null {
82+ const folderId = getSubfolderId ( 'actors' )
83+ if ( folderId ) return game . folders ?. get ( folderId ) || null
84+ return null
85+ }
86+
6787 /** Create a new chat session */
68- async createSession ( name ?: string ) : Promise < ChatSession > {
69- const folder = await this . getChatHistoryFolder ( )
88+ async createSession ( name ?: string , actorId ?: string , actorName ?: string ) : Promise < ChatSession > {
89+ // Actor roleplay sessions go in the Actors folder
90+ const folder = actorId ? ( this . getActorRoleplayFolder ( ) || await this . getChatHistoryFolder ( ) ) : await this . getChatHistoryFolder ( )
7091 const now = Date . now ( )
71- const sessionName = name || ` Chat ${ new Date ( now ) . toLocaleString ( ) } `
92+ const sessionName = name || ( actorName ? ` ${ actorName } — ${ new Date ( now ) . toLocaleString ( ) } ` : ` Chat ${ new Date ( now ) . toLocaleString ( ) } `)
7293
7394 const journal = await JournalEntry . create ( {
7495 name : sessionName ,
7596 folder : folder . id ,
7697 pages : [
7798 {
78- name : 'Chat Log' ,
99+ name : actorName ? ` ${ actorName } Roleplay` : 'Chat Log' ,
79100 type : 'text' ,
80101 text : { content : '' , format : 1 } ,
81102 } ,
82103 ] ,
83104 flags : {
84105 [ MODULE_ID ] : {
85- type : 'chat-session' ,
106+ type : actorId ? 'actor-roleplay' : 'chat-session' ,
86107 messages : [ ] ,
87108 createdAt : now ,
88109 updatedAt : now ,
89110 model : '' ,
90111 tokenCount : 0 ,
112+ ...( actorId ? { actorId, actorName } : { } ) ,
91113 } ,
92114 } ,
93115 } )
@@ -99,6 +121,8 @@ class ChatSessionManager {
99121 createdAt : now ,
100122 updatedAt : now ,
101123 model : '' ,
124+ actorId,
125+ actorName,
102126 }
103127 }
104128
@@ -178,7 +202,7 @@ class ChatSessionManager {
178202 if ( ! entry ) return null
179203
180204 const flags = entry . flags ?. [ MODULE_ID ] as Record < string , any >
181- if ( ! flags || flags . type !== 'chat-session' ) return null
205+ if ( ! flags || ( flags . type !== 'chat-session' && flags . type !== 'actor-roleplay' ) ) return null
182206
183207 return {
184208 id : entry . id ,
@@ -188,6 +212,8 @@ class ChatSessionManager {
188212 updatedAt : flags . updatedAt || 0 ,
189213 model : flags . model || '' ,
190214 tokenCount : flags . tokenCount ,
215+ actorId : flags . actorId ,
216+ actorName : flags . actorName ,
191217 }
192218 }
193219
@@ -199,7 +225,7 @@ class ChatSessionManager {
199225
200226 for ( const entry of game . journal . values ( ) ) {
201227 const flags = entry . flags ?. [ MODULE_ID ] as Record < string , any >
202- if ( ! flags || flags . type !== 'chat-session' ) continue
228+ if ( ! flags || ( flags . type !== 'chat-session' && flags . type !== 'actor-roleplay' ) ) continue
203229
204230 sessions . push ( {
205231 id : entry . id ,
@@ -208,6 +234,8 @@ class ChatSessionManager {
208234 updatedAt : flags . updatedAt || 0 ,
209235 messageCount : ( flags . messages || [ ] ) . length ,
210236 model : flags . model || '' ,
237+ actorId : flags . actorId ,
238+ actorName : flags . actorName ,
211239 } )
212240 }
213241
0 commit comments