@@ -19,6 +19,7 @@ import {
1919 relative as relativeHostPath ,
2020 resolve as resolveHostPath ,
2121} from "node:path" ;
22+ import { createRequire } from "node:module" ;
2223import { fileURLToPath } from "node:url" ;
2324import { type ToolKit , validateToolkits } from "./host-tools.js" ;
2425import { generateToolReference } from "./host-tools-prompt.js" ;
@@ -2537,23 +2538,7 @@ export class AgentOs {
25372538 }
25382539
25392540 private _resolvePackageBin ( packageName : string , binName ?: string ) : string {
2540- const vmPrefix = `/root/node_modules/${ packageName } ` ;
2541- let hostPkgJsonPath : string | null = null ;
2542- for ( const root of this . _softwareRoots ) {
2543- if ( root . vmPath === vmPrefix ) {
2544- hostPkgJsonPath = join ( root . hostPath , "package.json" ) ;
2545- break ;
2546- }
2547- }
2548- // Fall back to CWD-based node_modules.
2549- if ( ! hostPkgJsonPath ) {
2550- hostPkgJsonPath = join (
2551- this . _moduleAccessCwd ,
2552- "node_modules" ,
2553- packageName ,
2554- "package.json" ,
2555- ) ;
2556- }
2541+ const hostPkgJsonPath = this . _resolveHostPackageJson ( packageName ) ;
25572542 const pkg = JSON . parse ( readFileSync ( hostPkgJsonPath , "utf-8" ) ) ;
25582543
25592544 let binEntry : string | undefined ;
@@ -2575,16 +2560,7 @@ export class AgentOs {
25752560
25762561 private _resolvePackageDir ( packageName : string ) : string {
25772562 const vmPrefix = `/root/node_modules/${ packageName } ` ;
2578- let hostPackageDir : string | null = null ;
2579- for ( const root of this . _softwareRoots ) {
2580- if ( root . vmPath === vmPrefix ) {
2581- hostPackageDir = root . hostPath ;
2582- break ;
2583- }
2584- }
2585- if ( ! hostPackageDir ) {
2586- hostPackageDir = join ( this . _moduleAccessCwd , "node_modules" , packageName ) ;
2587- }
2563+ const hostPackageDir = dirname ( this . _resolveHostPackageJson ( packageName ) ) ;
25882564
25892565 const resolvedHostDir = realpathSync ( hostPackageDir ) ;
25902566 const repoNodeModules = join (
@@ -2612,6 +2588,62 @@ export class AgentOs {
26122588 return vmPrefix ;
26132589 }
26142590
2591+ /**
2592+ * Resolve the host-side package.json path for a package.
2593+ * Tries (in order):
2594+ * 1. Software roots (packages registered via `software: [...]`)
2595+ * 2. moduleAccessCwd/node_modules/<package>
2596+ * 3. Node require.resolve from each software root (finds dependencies of software packages)
2597+ * 4. Node require.resolve from moduleAccessCwd (handles pnpm hoisting)
2598+ */
2599+ private _resolveHostPackageJson ( packageName : string ) : string {
2600+ const vmPrefix = `/root/node_modules/${ packageName } ` ;
2601+
2602+ // 1. Check software roots for a direct match.
2603+ for ( const root of this . _softwareRoots ) {
2604+ if ( root . vmPath === vmPrefix ) {
2605+ return join ( root . hostPath , "package.json" ) ;
2606+ }
2607+ }
2608+
2609+ // 2. Check moduleAccessCwd/node_modules.
2610+ const cwdCandidate = join (
2611+ this . _moduleAccessCwd ,
2612+ "node_modules" ,
2613+ packageName ,
2614+ "package.json" ,
2615+ ) ;
2616+ if ( existsSync ( cwdCandidate ) ) {
2617+ return cwdCandidate ;
2618+ }
2619+
2620+ // 3. Try resolving from each software root's host directory.
2621+ // This finds dependencies of software packages (e.g. pi-acp
2622+ // as a dependency of @rivet -dev/agent-os-pi).
2623+ for ( const root of this . _softwareRoots ) {
2624+ try {
2625+ const req = createRequire ( join ( root . hostPath , "package.json" ) ) ;
2626+ return req . resolve ( `${ packageName } /package.json` ) ;
2627+ } catch {
2628+ // Not resolvable from this root, try next.
2629+ }
2630+ }
2631+
2632+ // 4. Try Node resolution from moduleAccessCwd.
2633+ try {
2634+ const req = createRequire (
2635+ join ( this . _moduleAccessCwd , "package.json" ) ,
2636+ ) ;
2637+ return req . resolve ( `${ packageName } /package.json` ) ;
2638+ } catch {
2639+ // Fall through to throw a descriptive error.
2640+ }
2641+
2642+ throw new Error (
2643+ `Cannot find package '${ packageName } '. Ensure it is installed or provided via the software option.` ,
2644+ ) ;
2645+ }
2646+
26152647 /**
26162648 * Resolve an agent config by ID. Package-provided configs take
26172649 * precedence over the hardcoded AGENT_CONFIGS.
0 commit comments