@@ -42,11 +42,12 @@ export function resolveSessionFilePathOptions(params: {
4242 agentId ?: string ;
4343 storePath ?: string ;
4444} ) : SessionFilePathOptions | undefined {
45+ const agentId = params . agentId ?. trim ( ) ;
4546 const storePath = params . storePath ?. trim ( ) ;
4647 if ( storePath ) {
47- return { sessionsDir : path . dirname ( path . resolve ( storePath ) ) } ;
48+ const sessionsDir = path . dirname ( path . resolve ( storePath ) ) ;
49+ return agentId ? { sessionsDir, agentId } : { sessionsDir } ;
4850 }
49- const agentId = params . agentId ?. trim ( ) ;
5051 if ( agentId ) {
5152 return { agentId } ;
5253 }
@@ -71,7 +72,51 @@ function resolveSessionsDir(opts?: SessionFilePathOptions): string {
7172 return resolveAgentSessionsDir ( opts ?. agentId ) ;
7273}
7374
74- function resolvePathWithinSessionsDir ( sessionsDir : string , candidate : string ) : string {
75+ function resolvePathFromAgentSessionsDir (
76+ agentSessionsDir : string ,
77+ candidateAbsPath : string ,
78+ ) : string | undefined {
79+ const agentBase = path . resolve ( agentSessionsDir ) ;
80+ const relative = path . relative ( agentBase , candidateAbsPath ) ;
81+ if ( ! relative || relative . startsWith ( ".." ) || path . isAbsolute ( relative ) ) {
82+ return undefined ;
83+ }
84+ return path . resolve ( agentBase , relative ) ;
85+ }
86+
87+ function resolveSiblingAgentSessionsDir (
88+ baseSessionsDir : string ,
89+ agentId : string ,
90+ ) : string | undefined {
91+ const resolvedBase = path . resolve ( baseSessionsDir ) ;
92+ if ( path . basename ( resolvedBase ) !== "sessions" ) {
93+ return undefined ;
94+ }
95+ const baseAgentDir = path . dirname ( resolvedBase ) ;
96+ const baseAgentsDir = path . dirname ( baseAgentDir ) ;
97+ if ( path . basename ( baseAgentsDir ) !== "agents" ) {
98+ return undefined ;
99+ }
100+ const rootDir = path . dirname ( baseAgentsDir ) ;
101+ return path . join ( rootDir , "agents" , normalizeAgentId ( agentId ) , "sessions" ) ;
102+ }
103+
104+ function extractAgentIdFromAbsoluteSessionPath ( candidateAbsPath : string ) : string | undefined {
105+ const normalized = path . normalize ( path . resolve ( candidateAbsPath ) ) ;
106+ const parts = normalized . split ( path . sep ) . filter ( Boolean ) ;
107+ const sessionsIndex = parts . lastIndexOf ( "sessions" ) ;
108+ if ( sessionsIndex < 2 || parts [ sessionsIndex - 2 ] !== "agents" ) {
109+ return undefined ;
110+ }
111+ const agentId = parts [ sessionsIndex - 1 ] ;
112+ return agentId || undefined ;
113+ }
114+
115+ function resolvePathWithinSessionsDir (
116+ sessionsDir : string ,
117+ candidate : string ,
118+ opts ?: { agentId ?: string } ,
119+ ) : string {
75120 const trimmed = candidate . trim ( ) ;
76121 if ( ! trimmed ) {
77122 throw new Error ( "Session file path must not be empty" ) ;
@@ -81,6 +126,34 @@ function resolvePathWithinSessionsDir(sessionsDir: string, candidate: string): s
81126 // Older versions stored absolute sessionFile paths in sessions.json;
82127 // convert them to relative so the containment check passes.
83128 const normalized = path . isAbsolute ( trimmed ) ? path . relative ( resolvedBase , trimmed ) : trimmed ;
129+ if ( normalized . startsWith ( ".." ) && path . isAbsolute ( trimmed ) ) {
130+ const tryAgentFallback = ( agentId : string ) : string | undefined => {
131+ const normalizedAgentId = normalizeAgentId ( agentId ) ;
132+ const siblingSessionsDir = resolveSiblingAgentSessionsDir ( resolvedBase , normalizedAgentId ) ;
133+ if ( siblingSessionsDir ) {
134+ const siblingResolved = resolvePathFromAgentSessionsDir ( siblingSessionsDir , trimmed ) ;
135+ if ( siblingResolved ) {
136+ return siblingResolved ;
137+ }
138+ }
139+ return resolvePathFromAgentSessionsDir ( resolveAgentSessionsDir ( normalizedAgentId ) , trimmed ) ;
140+ } ;
141+
142+ const explicitAgentId = opts ?. agentId ?. trim ( ) ;
143+ if ( explicitAgentId ) {
144+ const resolvedFromAgent = tryAgentFallback ( explicitAgentId ) ;
145+ if ( resolvedFromAgent ) {
146+ return resolvedFromAgent ;
147+ }
148+ }
149+ const extractedAgentId = extractAgentIdFromAbsoluteSessionPath ( trimmed ) ;
150+ if ( extractedAgentId ) {
151+ const resolvedFromPath = tryAgentFallback ( extractedAgentId ) ;
152+ if ( resolvedFromPath ) {
153+ return resolvedFromPath ;
154+ }
155+ }
156+ }
84157 if ( ! normalized || normalized . startsWith ( ".." ) || path . isAbsolute ( normalized ) ) {
85158 throw new Error ( "Session file path must be within sessions directory" ) ;
86159 }
@@ -122,7 +195,7 @@ export function resolveSessionFilePath(
122195 const sessionsDir = resolveSessionsDir ( opts ) ;
123196 const candidate = entry ?. sessionFile ?. trim ( ) ;
124197 if ( candidate ) {
125- return resolvePathWithinSessionsDir ( sessionsDir , candidate ) ;
198+ return resolvePathWithinSessionsDir ( sessionsDir , candidate , { agentId : opts ?. agentId } ) ;
126199 }
127200 return resolveSessionTranscriptPathInDir ( sessionId , sessionsDir ) ;
128201}
0 commit comments