@@ -225,7 +225,8 @@ export class ModuleAccessFileSystem implements VirtualFileSystem {
225225 ( a , b ) => b . vmPath . length - a . vmPath . length ,
226226 ) ;
227227
228- // Expand allowed roots to include package root host paths.
228+ // Expand allowed roots to include package root host paths and their
229+ // sibling node_modules (for transitive dep resolution in pnpm).
229230 for ( const root of this . packageRoots ) {
230231 try {
231232 const canonical = fsSync . realpathSync ( root . hostPath ) ;
@@ -242,6 +243,24 @@ export class ModuleAccessFileSystem implements VirtualFileSystem {
242243 }
243244 }
244245 }
246+ // Add the pnpm store root if the package is inside a .pnpm directory.
247+ // This makes all transitive deps in the store accessible.
248+ const canonicalParts = canonical . split ( path . sep ) ;
249+ const pnpmIdx = canonicalParts . indexOf ( ".pnpm" ) ;
250+ if ( pnpmIdx >= 0 ) {
251+ const pnpmStoreRoot = canonicalParts . slice ( 0 , pnpmIdx + 1 ) . join ( path . sep ) ;
252+ if ( ! this . overlayAllowedRoots . includes ( pnpmStoreRoot ) ) {
253+ this . overlayAllowedRoots . push ( pnpmStoreRoot ) ;
254+ }
255+ }
256+ // Also add the parent node_modules directory for non-pnpm setups.
257+ const parentNm = path . dirname ( root . hostPath ) ;
258+ try {
259+ const canonicalParent = fsSync . realpathSync ( parentNm ) ;
260+ if ( ! this . overlayAllowedRoots . includes ( canonicalParent ) ) {
261+ this . overlayAllowedRoots . push ( canonicalParent ) ;
262+ }
263+ } catch { /* skip */ }
245264 } catch {
246265 // Package root doesn't exist on host; skip.
247266 }
@@ -310,19 +329,62 @@ export class ModuleAccessFileSystem implements VirtualFileSystem {
310329 }
311330
312331 // Fall back to CWD-based node_modules.
313- if ( ! this . hostNodeModulesRoot ) {
314- return null ;
315- }
316- if ( virtualPath === SANDBOX_NODE_MODULES_ROOT ) {
317- return this . hostNodeModulesRoot ;
332+ if ( this . hostNodeModulesRoot ) {
333+ if ( virtualPath === SANDBOX_NODE_MODULES_ROOT ) {
334+ return this . hostNodeModulesRoot ;
335+ }
336+ const relative = path . posix
337+ . relative ( SANDBOX_NODE_MODULES_ROOT , virtualPath )
338+ . replace ( / ^ \/ + / , "" ) ;
339+ if ( ! relative ) {
340+ return this . hostNodeModulesRoot ;
341+ }
342+ const candidate = path . join ( this . hostNodeModulesRoot , ...relative . split ( "/" ) ) ;
343+ if ( fsSync . existsSync ( candidate ) ) {
344+ return candidate ;
345+ }
318346 }
319- const relative = path . posix
347+
348+ // Fall back: resolve transitive dependencies from package root siblings.
349+ // In pnpm, each package root sits in a node_modules/ dir alongside
350+ // its transitive deps (e.g., .pnpm/pi-agent@.../node_modules/chalk).
351+ // Check each package root's sibling node_modules for the requested package.
352+ const nmRelative = path . posix
320353 . relative ( SANDBOX_NODE_MODULES_ROOT , virtualPath )
321354 . replace ( / ^ \/ + / , "" ) ;
322- if ( ! relative ) {
323- return this . hostNodeModulesRoot ;
355+ if ( nmRelative ) {
356+ const relParts = nmRelative . split ( "/" ) ;
357+ const pkgName = relParts [ 0 ] . startsWith ( "@" )
358+ ? relParts . slice ( 0 , 2 ) . join ( "/" )
359+ : relParts [ 0 ] ;
360+ const subPath = relParts [ 0 ] . startsWith ( "@" )
361+ ? relParts . slice ( 2 ) . join ( "/" )
362+ : relParts . slice ( 1 ) . join ( "/" ) ;
363+ for ( const root of this . packageRoots ) {
364+ // root.hostPath is e.g. .../node_modules/@mariozechner /pi-coding-agent
365+ // Its parent node_modules dir may contain chalk, undici, etc.
366+ const siblingPkg = path . join ( path . dirname ( root . hostPath ) , pkgName ) ;
367+ try {
368+ if ( fsSync . existsSync ( siblingPkg ) ) {
369+ const realPkg = fsSync . realpathSync ( siblingPkg ) ;
370+ return subPath ? path . join ( realPkg , ...subPath . split ( "/" ) ) : realPkg ;
371+ }
372+ } catch { /* skip */ }
373+ // Also try the parent's parent for scoped packages
374+ const parentNm = path . dirname ( path . dirname ( root . hostPath ) ) ;
375+ if ( parentNm !== path . dirname ( root . hostPath ) ) {
376+ const parentSibling = path . join ( parentNm , pkgName ) ;
377+ try {
378+ if ( fsSync . existsSync ( parentSibling ) ) {
379+ const realPkg = fsSync . realpathSync ( parentSibling ) ;
380+ return subPath ? path . join ( realPkg , ...subPath . split ( "/" ) ) : realPkg ;
381+ }
382+ } catch { /* skip */ }
383+ }
384+ }
324385 }
325- return path . join ( this . hostNodeModulesRoot , ...relative . split ( "/" ) ) ;
386+
387+ return null ;
326388 }
327389
328390 private isProjectedHostPath ( pathValue : string ) : boolean {
0 commit comments