11import type { SessionIdentitySnapshot , SessionMetadataCacheEntry , SessionStoreInternals } from "./types.ts" ;
22import { asString , isRecord } from "./shared.ts" ;
33import { getResolvedOpenClawRootDir , importOpenClawInternalModule } from "./openclaw-runtime.ts" ;
4+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core" ;
45
5- const SESSION_META_CACHE_TTL_MS = 2_000 ;
6+ const SESSION_META_KNOWN_CACHE_TTL_MS = 60_000 ;
7+ const SESSION_META_UNKNOWN_CACHE_TTL_MS = 2_000 ;
68const SESSION_META_CACHE_MAX = 512 ;
79
810let sessionStoreInternalsPromise : Promise < SessionStoreInternals > | null = null ;
911const sessionMetadataCache = new Map < string , SessionMetadataCacheEntry > ( ) ;
1012
11- async function loadSessionStoreInternals ( ) : Promise < SessionStoreInternals > {
13+ function resolveRuntimeSessionStoreInternals (
14+ api : OpenClawPluginApi | undefined ,
15+ ) : SessionStoreInternals | null {
16+ const runtime = isRecord ( api ?. runtime ) ? api . runtime : undefined ;
17+ const runtimeConfig = isRecord ( runtime ?. config ) ? runtime . config : undefined ;
18+ const runtimeAgent = isRecord ( runtime ?. agent ) ? runtime . agent : undefined ;
19+ const runtimeAgentSession = isRecord ( runtimeAgent ?. session ) ? runtimeAgent . session : undefined ;
20+
21+ const resolveStorePath = runtimeAgentSession ?. resolveStorePath ;
22+ const loadSessionStore = runtimeAgentSession ?. loadSessionStore ;
23+ if ( typeof resolveStorePath !== "function" || typeof loadSessionStore !== "function" ) {
24+ return null ;
25+ }
26+
27+ const loadConfig = runtimeConfig ?. loadConfig ;
28+ const fallbackConfig = isRecord ( api ?. config ) ? api . config : { } ;
29+ return {
30+ loadConfig :
31+ typeof loadConfig === "function"
32+ ? ( loadConfig as SessionStoreInternals [ "loadConfig" ] )
33+ : ( ) => fallbackConfig ,
34+ resolveStorePath : resolveStorePath as SessionStoreInternals [ "resolveStorePath" ] ,
35+ loadSessionStore : loadSessionStore as SessionStoreInternals [ "loadSessionStore" ] ,
36+ } ;
37+ }
38+
39+ async function loadSessionStoreInternals (
40+ api : OpenClawPluginApi | undefined ,
41+ ) : Promise < SessionStoreInternals > {
42+ const runtimeInternals = resolveRuntimeSessionStoreInternals ( api ) ;
43+ if ( runtimeInternals ) {
44+ return runtimeInternals ;
45+ }
46+
1247 if ( sessionStoreInternalsPromise ) {
1348 return sessionStoreInternalsPromise ;
1449 }
@@ -50,6 +85,19 @@ async function loadSessionStoreInternals(): Promise<SessionStoreInternals> {
5085 return sessionStoreInternalsPromise ;
5186}
5287
88+ export async function warmSessionIdentityResolver ( params : {
89+ api : OpenClawPluginApi ;
90+ sourceAgentId ?: string ;
91+ } ) : Promise < void > {
92+ const internals = await loadSessionStoreInternals ( params . api ) ;
93+ const cfg = internals . loadConfig ( ) ;
94+ const sessionCfg = isRecord ( cfg . session ) ? cfg . session : undefined ;
95+ const storePath = internals . resolveStorePath ( asString ( sessionCfg ?. store ) , {
96+ agentId : asString ( params . sourceAgentId ) ,
97+ } ) ;
98+ internals . loadSessionStore ( storePath ) ;
99+ }
100+
53101function unknownSessionIdentity ( ) : SessionIdentitySnapshot {
54102 return {
55103 provider : null ,
@@ -80,6 +128,17 @@ function resolveBaseSessionKey(sessionKey: string): string {
80128 return base || sessionKey ;
81129}
82130
131+ function resolveSessionAgentId (
132+ normalizedSessionKey : string ,
133+ sourceAgentId : string | undefined ,
134+ ) : string | undefined {
135+ const parts = normalizedSessionKey . split ( ":" ) . filter ( Boolean ) ;
136+ if ( parts . length >= 2 && parts [ 0 ] === "agent" && parts [ 1 ] ) {
137+ return parts [ 1 ] ;
138+ }
139+ return asString ( sourceAgentId ) ;
140+ }
141+
83142function readSessionIdentityFromEntry ( entry : Record < string , unknown > ) : SessionIdentitySnapshot {
84143 const origin = isRecord ( entry . origin ) ? entry . origin : undefined ;
85144 const deliveryContext = isRecord ( entry . deliveryContext ) ? entry . deliveryContext : undefined ;
@@ -120,7 +179,16 @@ function readSessionIdentityFromEntry(entry: Record<string, unknown>): SessionId
120179}
121180
122181function setSessionMetadataCache ( key : string , data : SessionIdentitySnapshot ) : void {
123- sessionMetadataCache . set ( key , { at : Date . now ( ) , data } ) ;
182+ const now = Date . now ( ) ;
183+ sessionMetadataCache . set ( key , {
184+ at : now ,
185+ data,
186+ expiresAt :
187+ now +
188+ ( data . source === "sessionStore"
189+ ? SESSION_META_KNOWN_CACHE_TTL_MS
190+ : SESSION_META_UNKNOWN_CACHE_TTL_MS ) ,
191+ } ) ;
124192 if ( sessionMetadataCache . size > SESSION_META_CACHE_MAX ) {
125193 const oldest = sessionMetadataCache . keys ( ) . next ( ) . value ;
126194 if ( typeof oldest === "string" ) {
@@ -129,36 +197,76 @@ function setSessionMetadataCache(key: string, data: SessionIdentitySnapshot): vo
129197 }
130198}
131199
132- export async function resolveSessionIdentity (
133- sessionKey : string | undefined ,
134- ) : Promise < SessionIdentitySnapshot > {
135- const normalizedKey = normalizeSessionStoreKey ( sessionKey ) ;
136- if ( ! normalizedKey ) {
137- return unknownSessionIdentity ( ) ;
138- }
139-
140- const cached = sessionMetadataCache . get ( normalizedKey ) ;
141- if ( cached && Date . now ( ) - cached . at < SESSION_META_CACHE_TTL_MS ) {
142- return cached . data ;
200+ function setSessionMetadataCachePromise (
201+ key : string ,
202+ promise : Promise < SessionIdentitySnapshot > ,
203+ ) : void {
204+ sessionMetadataCache . set ( key , { at : Date . now ( ) , promise } ) ;
205+ if ( sessionMetadataCache . size > SESSION_META_CACHE_MAX ) {
206+ const oldest = sessionMetadataCache . keys ( ) . next ( ) . value ;
207+ if ( typeof oldest === "string" && oldest !== key ) {
208+ sessionMetadataCache . delete ( oldest ) ;
209+ }
143210 }
211+ }
144212
213+ async function readSessionIdentity ( params : {
214+ api ?: OpenClawPluginApi ;
215+ normalizedKey : string ;
216+ sourceAgentId ?: string ;
217+ } ) : Promise < SessionIdentitySnapshot > {
145218 try {
146- const internals = await loadSessionStoreInternals ( ) ;
219+ const internals = await loadSessionStoreInternals ( params . api ) ;
147220 const cfg = internals . loadConfig ( ) ;
148221 const sessionCfg = isRecord ( cfg . session ) ? cfg . session : undefined ;
149- const storePath = internals . resolveStorePath ( asString ( sessionCfg ?. store ) ) ;
222+ const storeAgentId = resolveSessionAgentId ( params . normalizedKey , params . sourceAgentId ) ;
223+ const storePath = internals . resolveStorePath ( asString ( sessionCfg ?. store ) , {
224+ agentId : storeAgentId ,
225+ } ) ;
150226 const store = internals . loadSessionStore ( storePath ) ;
151- const directEntry = store [ normalizedKey ] ;
152- const baseEntry = store [ resolveBaseSessionKey ( normalizedKey ) ] ;
227+ const directEntry = store [ params . normalizedKey ] ;
228+ const baseEntry = store [ resolveBaseSessionKey ( params . normalizedKey ) ] ;
153229 const entry : Record < string , unknown > | undefined = isRecord ( directEntry )
154230 ? directEntry
155231 : isRecord ( baseEntry )
156232 ? baseEntry
157233 : undefined ;
158- const data = entry ? readSessionIdentityFromEntry ( entry ) : unknownSessionIdentity ( ) ;
159- setSessionMetadataCache ( normalizedKey , data ) ;
160- return data ;
234+ return entry ? readSessionIdentityFromEntry ( entry ) : unknownSessionIdentity ( ) ;
161235 } catch {
162236 return unknownSessionIdentity ( ) ;
163237 }
164238}
239+
240+ export async function resolveSessionIdentity (
241+ input :
242+ | string
243+ | undefined
244+ | {
245+ api ?: OpenClawPluginApi ;
246+ sessionKey ?: string ;
247+ sourceAgentId ?: string ;
248+ } ,
249+ ) : Promise < SessionIdentitySnapshot > {
250+ const sessionKey = typeof input === "object" ? input . sessionKey : input ;
251+ const api = typeof input === "object" ? input . api : undefined ;
252+ const sourceAgentId = typeof input === "object" ? input . sourceAgentId : undefined ;
253+ const normalizedKey = normalizeSessionStoreKey ( sessionKey ) ;
254+ if ( ! normalizedKey ) {
255+ return unknownSessionIdentity ( ) ;
256+ }
257+
258+ const cached = sessionMetadataCache . get ( normalizedKey ) ;
259+ if ( cached ?. data && cached . expiresAt && Date . now ( ) < cached . expiresAt ) {
260+ return cached . data ;
261+ }
262+ if ( cached ?. promise ) {
263+ return cached . promise ;
264+ }
265+
266+ const promise = readSessionIdentity ( { api, normalizedKey, sourceAgentId } ) . then ( ( data ) => {
267+ setSessionMetadataCache ( normalizedKey , data ) ;
268+ return data ;
269+ } ) ;
270+ setSessionMetadataCachePromise ( normalizedKey , promise ) ;
271+ return promise ;
272+ }
0 commit comments