@@ -946,6 +946,14 @@ async function withEnv<T>(vars: Record<string, string | undefined>, fn: () => Pr
946946 }
947947}
948948
949+ function createFakePathExecutable ( dir : string , name : string ) : string {
950+ fs . mkdirSync ( dir , { recursive : true } ) ;
951+ const executablePath = path . join ( dir , process . platform === "win32" ? `${ name } .cmd` : name ) ;
952+ fs . writeFileSync ( executablePath , process . platform === "win32" ? "@echo off\r\n" : "#!/bin/sh\n" ) ;
953+ if ( process . platform !== "win32" ) fs . chmodSync ( executablePath , 0o755 ) ;
954+ return executablePath ;
955+ }
956+
949957describe ( "adeRpcServer" , ( ) => {
950958 it ( "treats requested privileged roles as external without trusted env identity" , async ( ) => {
951959 const { runtime } = createRuntime ( ) ;
@@ -1805,15 +1813,19 @@ describe("adeRpcServer", () => {
18051813
18061814 it ( "routes spawn_agent to lane-scoped tracked pty sessions" , async ( ) => {
18071815 const fixture = createRuntime ( ) ;
1816+ const binDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "ade-cli-spawn-bin-" ) ) ;
1817+ const claudePath = createFakePathExecutable ( binDir , "claude" ) ;
18081818 const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
18091819
1810- await initialize ( handler , { role : "orchestrator" } ) ;
1811- const response = await callTool ( handler , "spawn_agent" , {
1812- laneId : "lane-1" ,
1813- provider : "claude" ,
1814- model : "claude-sonnet-4-6" ,
1815- prompt : "Implement API wiring" ,
1816- title : "Orchestrator Spawn"
1820+ const response = await withEnv ( { PATH : `${ binDir } ${ path . delimiter } ${ process . env . PATH ?? "" } ` } , async ( ) => {
1821+ await initialize ( handler , { role : "orchestrator" } ) ;
1822+ return await callTool ( handler , "spawn_agent" , {
1823+ laneId : "lane-1" ,
1824+ provider : "claude" ,
1825+ model : "claude-sonnet-4-6" ,
1826+ prompt : "Implement API wiring" ,
1827+ title : "Orchestrator Spawn"
1828+ } ) ;
18171829 } ) ;
18181830
18191831 expect ( response ?. isError ) . toBeUndefined ( ) ;
@@ -1824,7 +1836,7 @@ describe("adeRpcServer", () => {
18241836 rows : 36 ,
18251837 tracked : true ,
18261838 toolType : "claude-orchestrated" ,
1827- command : "claude" ,
1839+ command : claudePath ,
18281840 args : expect . arrayContaining ( [ "--model" , "claude-sonnet-4-6" , "--permission-mode" , "default" , "Implement API wiring" ] ) ,
18291841 env : expect . objectContaining ( {
18301842 ADE_DEFAULT_ROLE : "agent" ,
@@ -1841,17 +1853,21 @@ describe("adeRpcServer", () => {
18411853 it ( "starts spawn_agent without writing an attached ADE server config" , async ( ) => {
18421854 const fixture = createRuntime ( ) ;
18431855 fixture . runtime . workspaceRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "ade-cli-spawn-workspace-" ) ) ;
1856+ const binDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "ade-cli-spawn-bin-" ) ) ;
1857+ const claudePath = createFakePathExecutable ( binDir , "claude" ) ;
18441858 const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
18451859
1846- await initialize ( handler , { role : "orchestrator" , runId : "run-from-identity" } ) ;
1847- const response = await callTool ( handler , "spawn_agent" , {
1848- laneId : "lane-1" ,
1849- provider : "claude" ,
1850- model : "claude-sonnet-4-6" ,
1851- prompt : "Implement API wiring" ,
1852- title : "Orchestrator Spawn" ,
1853- runId : "run-1" ,
1854- attemptId : "attempt-workspace-roots"
1860+ const response = await withEnv ( { PATH : `${ binDir } ${ path . delimiter } ${ process . env . PATH ?? "" } ` } , async ( ) => {
1861+ await initialize ( handler , { role : "orchestrator" , runId : "run-from-identity" } ) ;
1862+ return await callTool ( handler , "spawn_agent" , {
1863+ laneId : "lane-1" ,
1864+ provider : "claude" ,
1865+ model : "claude-sonnet-4-6" ,
1866+ prompt : "Implement API wiring" ,
1867+ title : "Orchestrator Spawn" ,
1868+ runId : "run-1" ,
1869+ attemptId : "attempt-workspace-roots"
1870+ } ) ;
18551871 } ) ;
18561872
18571873 expect ( response ?. isError ) . toBeUndefined ( ) ;
@@ -1860,7 +1876,7 @@ describe("adeRpcServer", () => {
18601876 expect ( response . structuredContent . startupCommand ) . toContain ( "ADE_ATTEMPT_ID=attempt-workspace-roots" ) ;
18611877 expect ( fixture . runtime . ptyService . create ) . toHaveBeenCalledWith (
18621878 expect . objectContaining ( {
1863- command : "claude" ,
1879+ command : claudePath ,
18641880 env : expect . objectContaining ( {
18651881 ADE_RUN_ID : "run-1" ,
18661882 ADE_ATTEMPT_ID : "attempt-workspace-roots" ,
@@ -1870,6 +1886,32 @@ describe("adeRpcServer", () => {
18701886 ) ;
18711887 } ) ;
18721888
1889+ it ( "keeps spawn_agent on shell startup when the provider executable cannot be resolved" , async ( ) => {
1890+ const fixture = createRuntime ( ) ;
1891+ const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
1892+
1893+ const response = await withEnv ( { PATH : fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "ade-cli-empty-path-" ) ) } , async ( ) => {
1894+ await initialize ( handler , { role : "orchestrator" } ) ;
1895+ return await callTool ( handler , "spawn_agent" , {
1896+ laneId : "lane-1" ,
1897+ provider : "claude" ,
1898+ prompt : "Implement API wiring" ,
1899+ } ) ;
1900+ } ) ;
1901+
1902+ expect ( response ?. isError ) . toBeUndefined ( ) ;
1903+ expect ( fixture . runtime . ptyService . create ) . toHaveBeenCalledWith (
1904+ expect . not . objectContaining ( {
1905+ command : expect . any ( String ) ,
1906+ } )
1907+ ) ;
1908+ expect ( fixture . runtime . ptyService . create ) . toHaveBeenCalledWith (
1909+ expect . objectContaining ( {
1910+ startupCommand : expect . stringContaining ( "claude" ) ,
1911+ } )
1912+ ) ;
1913+ } ) ;
1914+
18731915 it ( "rejects config-toml permission mode for Claude spawn_agent sessions" , async ( ) => {
18741916 const fixture = createRuntime ( ) ;
18751917 const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
0 commit comments